More fleshing out of Merge framework and backends.

This commit is contained in:
Jim Evins
2016-05-07 16:17:29 -04:00
parent 2ff07f01b3
commit 88e32d9738
11 changed files with 539 additions and 112 deletions
+2 -1
View File
@@ -33,11 +33,12 @@ set (glabels_sources
LabelRegion.cpp LabelRegion.cpp
MainWindow.cpp MainWindow.cpp
Merge.cpp Merge.cpp
MergeField.cpp MergeFactory.cpp
MergeView.cpp MergeView.cpp
MergeRecord.cpp MergeRecord.cpp
MergeNone.cpp MergeNone.cpp
MergeText.cpp MergeText.cpp
MergeTextCsv.cpp
ObjectEditor.cpp ObjectEditor.cpp
Outline.cpp Outline.cpp
PageRenderer.cpp PageRenderer.cpp
+84
View File
@@ -0,0 +1,84 @@
/* MergeFactory.cpp
*
* Copyright (C) 2016 Jim Evins <evins@snaught.com>
*
* This file is part of gLabels-qt.
*
* gLabels-qt is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gLabels-qt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gLabels-qt. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MergeFactory.h"
#include "MergeNone.h"
#include "MergeTextCsv.h"
///
/// Static data
///
QMap<QString,MergeFactory::BackendEntry> MergeFactory::mBackendMap;
///
/// Constructor
///
MergeFactory::MergeFactory()
{
registerBackend( "None", &MergeNone::create );
registerBackend( "Text/CSV", &MergeTextCsv::create );
}
///
/// Initialize
///
void MergeFactory::init()
{
static MergeFactory* singletonInstance = 0;
if ( !singletonInstance )
{
singletonInstance = new MergeFactory();
}
}
///
/// Create Merge object
///
Merge* MergeFactory::createMerge( const QString& id )
{
QMap<QString,BackendEntry>::iterator iBackend = mBackendMap.find( id );
if ( iBackend != mBackendMap.end() )
{
return iBackend->create();
}
return MergeNone::create();
}
///
/// Register backend
///
void MergeFactory::registerBackend( const QString& id, CreateFct create )
{
BackendEntry backend;
backend.id = id;
backend.create = create;
mBackendMap[ id ] = backend;
}
+72
View File
@@ -0,0 +1,72 @@
/* MergeFactory.h
*
* Copyright (C) 2016 Jim Evins <evins@snaught.com>
*
* This file is part of gLabels-qt.
*
* gLabels-qt is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gLabels-qt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gLabels-qt. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MergeFactory_h
#define MergeFactory_h
#include "Merge.h"
///
/// MergeFactory
///
struct MergeFactory
{
/////////////////////////////////
// Life Cycle
/////////////////////////////////
protected:
MergeFactory();
/////////////////////////////////
// Static methods
/////////////////////////////////
public:
static void init();
static Merge* createMerge( const QString& id );
/////////////////////////////////
// private methods
/////////////////////////////////
private:
typedef Merge* (*CreateFct)();
static void registerBackend( const QString& id, CreateFct create );
/////////////////////////////////
// private data
/////////////////////////////////
class BackendEntry
{
public:
QString id;
CreateFct create;
};
static QMap<QString,BackendEntry> mBackendMap;
};
#endif // MergeFactory_h
+9
View File
@@ -54,6 +54,15 @@ MergeNone* MergeNone::clone() const
} }
///
/// Create
///
Merge* MergeNone::create()
{
return new MergeNone();
}
/// ///
/// Get key list /// Get key list
/// ///
+7
View File
@@ -45,6 +45,13 @@ protected:
MergeNone* clone() const; MergeNone* clone() const;
/////////////////////////////////
// Static methods
/////////////////////////////////
public:
static Merge* create();
///////////////////////////////// /////////////////////////////////
// Implementation of virtual methods // Implementation of virtual methods
///////////////////////////////// /////////////////////////////////
+2 -29
View File
@@ -1,6 +1,6 @@
/* MergeRecord.cpp /* MergeRecord.cpp
* *
* Copyright (C) 2013 Jim Evins <evins@snaught.com> * Copyright (C) 2013-2016 Jim Evins <evins@snaught.com>
* *
* This file is part of gLabels-qt. * This file is part of gLabels-qt.
* *
@@ -33,7 +33,7 @@ MergeRecord::MergeRecord() : mSelected( false )
/// Constructor /// Constructor
/// ///
MergeRecord::MergeRecord( const MergeRecord* record ) MergeRecord::MergeRecord( const MergeRecord* record )
: mSelected(record->mSelected), mFieldList(record->mFieldList) : QMap<QString,QString>(*record), mSelected(record->mSelected)
{ {
} }
@@ -63,30 +63,3 @@ void MergeRecord::setSelected( bool value )
{ {
mSelected = value; mSelected = value;
} }
///
/// Is record empty?
///
bool MergeRecord::isEmpty() const
{
return mFieldList.size() == 0;
}
///
/// Get field list
///
const QList<MergeField>& MergeRecord::fieldList() const
{
return mFieldList;
}
///
/// Set field list
///
void MergeRecord::setFieldList( QList<MergeField>& value )
{
mFieldList = value;
}
+7 -12
View File
@@ -1,6 +1,6 @@
/* MergeRecord.h /* MergeRecord.h
* *
* Copyright (C) 2013 Jim Evins <evins@snaught.com> * Copyright (C) 2013-2016 Jim Evins <evins@snaught.com>
* *
* This file is part of gLabels-qt. * This file is part of gLabels-qt.
* *
@@ -22,16 +22,15 @@
#define MergeRecord_h #define MergeRecord_h
#include <QString> #include <QString>
#include <QList> #include <QMap>
#include "MergeField.h"
/// ///
/// Merge Record Structure /// Merge Record
/// ///
struct MergeRecord struct MergeRecord : public QMap<QString,QString>
{ {
///////////////////////////////// /////////////////////////////////
// Life Cycle // Life Cycle
///////////////////////////////// /////////////////////////////////
@@ -52,18 +51,14 @@ public:
public: public:
bool isSelected() const; bool isSelected() const;
void setSelected( bool value ); void setSelected( bool value );
bool isEmpty() const;
const QList<MergeField>& fieldList() const;
void setFieldList( QList<MergeField>& value );
///////////////////////////////// /////////////////////////////////
// Private data // Private data
///////////////////////////////// /////////////////////////////////
private: private:
bool mSelected; bool mSelected;
QList<MergeField> mFieldList;
}; };
+305 -6
View File
@@ -1,6 +1,6 @@
/* MergeText.cpp /* MergeText.cpp
* *
* Copyright (C) 2015 Jim Evins <evins@snaught.com> * Copyright (C) 2016 Jim Evins <evins@snaught.com>
* *
* This file is part of gLabels-qt. * This file is part of gLabels-qt.
* *
@@ -24,7 +24,7 @@
/// ///
/// Constructor /// Constructor
/// ///
MergeText::MergeText( QChar delimiter, bool line1HasKeys ) : Merge( Merge::FILE ) MergeText::MergeText( QChar delimiter, bool line1HasKeys ) : Merge( Merge::FILE ), mNFieldsMax(0)
{ {
} }
@@ -51,8 +51,12 @@ MergeText::~MergeText()
/// ///
QList<QString> MergeText::keyList() const QList<QString> MergeText::keyList() const
{ {
QList<QString> emptyList; QList<QString> keys;
return emptyList; for ( int iField = 0; iField < mNFieldsMax; iField++ )
{
keys << keyFromIndex(iField);
}
return keys;
} }
@@ -61,7 +65,7 @@ QList<QString> MergeText::keyList() const
/// ///
QString MergeText::primaryKey() const QString MergeText::primaryKey() const
{ {
return ""; keyFromIndex(0);
} }
@@ -73,9 +77,15 @@ void MergeText::open()
mFile.setFileName( source() ); mFile.setFileName( source() );
mFile.open( QIODevice::ReadOnly|QIODevice::Text ); mFile.open( QIODevice::ReadOnly|QIODevice::Text );
mKeys.clear();
if ( mLine1HasKeys && mFile.isOpen() ) if ( mLine1HasKeys && mFile.isOpen() )
{ {
// Todo parse line #1, create key list from string list mKeys = parseLine();
if ( (mKeys.size() == 1) && (mKeys[0] == "") )
{
mKeys.clear();
}
} }
} }
@@ -97,5 +107,294 @@ void MergeText::close()
/// ///
MergeRecord* MergeText::readNextRecord() MergeRecord* MergeText::readNextRecord()
{ {
QList<QString> values = parseLine();
if ( !values.isEmpty() )
{
MergeRecord* record = new MergeRecord();
int iField = 0;
foreach ( QString value, values )
{
(*record)[ keyFromIndex(iField) ] = value;
iField++;
}
return record;
}
return 0; return 0;
} }
///
/// Key from field index
///
QString MergeText::keyFromIndex( int iField ) const
{
if ( mLine1HasKeys && ( iField < mKeys.size() ) )
{
return mKeys[iField];
}
else
{
return QString::number( iField+1 );
}
}
///
/// Parse line.
///
/// Attempt to be a robust parser of various CSV (and similar) formats.
///
/// Based on CSV format described in RFC 4180 section 2.
///
/// Additions to RFC 4180 rules:
/// - delimeters and other special characters may be "escaped" by a leading
/// backslash (\)
/// - C escape sequences for newline (\n) and tab (\t) are also translated.
/// - if quoted text is not followed by a delimeter, any additional text is
/// concatenated with quoted portion.
///
/// Returns a list of fields. A blank line is considered a line with one
/// empty field. Returns an empty list when done.
///
QList<QString> MergeText::parseLine()
{
QList<QString> fields;
enum State
{
DELIM, QUOTED, QUOTED_QUOTE1, QUOTED_ESCAPED, SIMPLE, SIMPLE_ESCAPED, DONE
} state = DELIM;
QByteArray field;
while ( state != DONE )
{
char c;
if ( mFile.getChar( &c ) )
{
switch (state)
{
case DELIM:
switch (c)
{
case '\n':
/* last field is empty. */
fields << "";
state = DONE;
break;
case '\r':
/* ignore */
state = DELIM;
break;
case '"':
/* start a quoted field. */
state = QUOTED;
break;
case '\\':
/* simple field, but 1st character is an escape. */
state = SIMPLE_ESCAPED;
break;
default:
if ( c == mDelimeter )
{
/* field is empty. */
fields << "";
state = DELIM;
}
else
{
/* begining of a simple field. */
field.append( c );
state = SIMPLE;
}
break;
}
break;
case QUOTED:
switch (c)
{
case '"':
/* Possible end of field, but could be 1st of a pair. */
state = QUOTED_QUOTE1;
break;
case '\\':
/* Escape next character, or special escape, e.g. \n. */
state = QUOTED_ESCAPED;
break;
default:
/* Use character literally. */
field.append( c );
break;
}
break;
case QUOTED_QUOTE1:
switch (c)
{
case '\n':
/* line ended after quoted item */
fields << QString( field );
state = DONE;
break;
case '"':
/* second quote, insert and stay quoted. */
field.append( c );
state = QUOTED;
break;
case '\r':
/* ignore and go to fallback */
state = SIMPLE;
break;
default:
if ( c == mDelimeter )
{
/* end of field. */
fields << QString( field );
field.clear();
state = DELIM;
}
else
{
/* fallback if not a delim or another quote. */
field.append( c );
state = SIMPLE;
}
break;
}
break;
case QUOTED_ESCAPED:
switch (c)
{
case 'n':
/* Decode "\n" as newline. */
field.append( '\n' );
state = QUOTED;
break;
case 't':
/* Decode "\t" as tab. */
field.append( '\t' );
state = QUOTED;
break;
default:
/* Use character literally. */
field.append( c );
state = QUOTED;
break;
}
break;
case SIMPLE:
switch (c)
{
case '\n':
/* line ended */
fields << QString( field );
state = DONE;
break;
case '\r':
/* ignore */
state = SIMPLE;
break;
case '\\':
/* Escape next character, or special escape, e.g. \n. */
state = SIMPLE_ESCAPED;
break;
default:
if ( c == mDelimeter )
{
/* end of field. */
fields << QString( field );
field.clear();
state = DELIM;
}
else
{
/* Use character literally. */
field.append( c );
state = SIMPLE;
}
break;
}
break;
case SIMPLE_ESCAPED:
switch (c)
{
case 'n':
/* Decode "\n" as newline. */
field.append( '\n' );
state = SIMPLE;
break;
case 't':
/* Decode "\t" as tab. */
field.append( '\t' );
state = SIMPLE;
break;
default:
/* Use character literally. */
field.append( (char)c );
state = SIMPLE;
break;
}
break;
default:
qWarning( "MergeText::parseLine()::Should not be reached! #1" );
break;
}
}
else
{
/* Handle EOF (could also be an error while reading). */
switch (state)
{
case DELIM:
/* EOF, no more lines. */
break;
case QUOTED:
/* File ended midway through quoted item. Truncate field. */
fields << QString( field );
break;
case QUOTED_QUOTE1:
/* File ended after quoted item. */
fields << QString( field );
break;
case QUOTED_ESCAPED:
/* File ended midway through quoted item. Truncate field. */
fields << QString( field );
break;
case SIMPLE:
/* File ended after simple item. */
fields << QString( field );
break;
case SIMPLE_ESCAPED:
/* File ended midway through escaped item. */
fields << QString( field );
break;
default:
qWarning( "MergeText::parseLine()::Should not be reached! #2" );
break;
}
state = DONE;
}
}
return fields;
}
+11 -2
View File
@@ -1,6 +1,6 @@
/* MergeText.h /* MergeText.h
* *
* Copyright (C) 2015 Jim Evins <evins@snaught.com> * Copyright (C) 2016 Jim Evins <evins@snaught.com>
* *
* This file is part of gLabels-qt. * This file is part of gLabels-qt.
* *
@@ -53,6 +53,13 @@ protected:
MergeRecord* readNextRecord(); MergeRecord* readNextRecord();
/////////////////////////////////
// Private methods
/////////////////////////////////
QString keyFromIndex( int iField ) const;
QList<QString> parseLine();
///////////////////////////////// /////////////////////////////////
// Private data // Private data
///////////////////////////////// /////////////////////////////////
@@ -60,7 +67,9 @@ private:
QChar mDelimeter; QChar mDelimeter;
bool mLine1HasKeys; bool mLine1HasKeys;
QFile mFile; QFile mFile;
QList<QString> mKeys;
int mNFieldsMax;
}; };
@@ -1,6 +1,6 @@
/* MergeField.cpp /* MergeTextCsv.cpp
* *
* Copyright (C) 2013 Jim Evins <evins@snaught.com> * Copyright (C) 2016 Jim Evins <evins@snaught.com>
* *
* This file is part of gLabels-qt. * This file is part of gLabels-qt.
* *
@@ -18,13 +18,13 @@
* along with gLabels-qt. If not, see <http://www.gnu.org/licenses/>. * along with gLabels-qt. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "MergeField.h" #include "MergeTextCsv.h"
/// ///
/// Default constructor /// Constructor
/// ///
MergeField::MergeField() MergeTextCsv::MergeTextCsv() : MergeText(',',false)
{ {
} }
@@ -32,44 +32,32 @@ MergeField::MergeField()
/// ///
/// Constructor /// Constructor
/// ///
MergeField::MergeField( const QString& key, const QString& value ) MergeTextCsv::MergeTextCsv( const MergeTextCsv* merge ) : MergeText( merge )
{ {
mKey = key;
mValue = value;
} }
/// ///
/// Get key /// Destructor
/// ///
const QString MergeField::key( void ) const MergeTextCsv::~MergeTextCsv()
{ {
return mKey;
} }
/// ///
/// Set key /// Clone
/// ///
void MergeField::setKey( const QString& value ) MergeTextCsv* MergeTextCsv::clone() const
{ {
mKey = value; return new MergeTextCsv( this );
} }
/// ///
/// Get value /// Create
/// ///
const QString MergeField::value( void ) const Merge* MergeTextCsv::create()
{ {
return mValue; return new MergeTextCsv();
}
///
/// Set value
///
void MergeField::setValue( const QString& value )
{
mValue = value;
} }
+26 -36
View File
@@ -1,6 +1,6 @@
/* MergeField.h /* MergeTextCsv.h
* *
* Copyright (C) 2013 Jim Evins <evins@snaught.com> * Copyright (C) 2016 Jim Evins <evins@snaught.com>
* *
* This file is part of gLabels-qt. * This file is part of gLabels-qt.
* *
@@ -18,51 +18,41 @@
* along with gLabels-qt. If not, see <http://www.gnu.org/licenses/>. * along with gLabels-qt. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef MergeField_h #ifndef MergeTextCsv_h
#define MergeField_h #define MergeTextCsv_h
#include <QString> #include "MergeText.h"
/// ///
/// Merge Field Structure /// MergeTextCsv Backend
/// ///
struct MergeField struct MergeTextCsv : public MergeText
{ {
///////////////////////////////// /////////////////////////////////
// Life Cycle // Life Cycle
///////////////////////////////// /////////////////////////////////
public:
MergeField();
MergeField( const QString& key, const QString& value );
/////////////////////////////////
// Properties
/////////////////////////////////
public:
//
// Key Property
//
const QString key( void ) const;
void setKey( const QString& value );
//
// Value Property
//
const QString value( void ) const;
void setValue( const QString& value );
/////////////////////////////////
// Private data
/////////////////////////////////
private: private:
QString mKey; MergeTextCsv();
QString mValue; MergeTextCsv( const MergeTextCsv* merge );
virtual ~MergeTextCsv();
/////////////////////////////////
// Object duplication
/////////////////////////////////
public:
MergeTextCsv* clone() const;
/////////////////////////////////
// Static methods
/////////////////////////////////
public:
static Merge* create();
}; };
#endif // MergeField_h #endif // MergeTextCsv_h