11516 lines
316 KiB
C++
11516 lines
316 KiB
C++
/*
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2008, University in Hagen,
|
|
Faculty of Mathematics and Computer Science,
|
|
Database Systems for New Applications.
|
|
|
|
SECONDO 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 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SECONDO 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 SECONDO; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
----
|
|
|
|
//paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}]
|
|
//paragraph [10] Footnote: [{\footnote{] [}}]
|
|
//[ue] [\"u]
|
|
//[ae] [\"a]
|
|
//[_] [\_]
|
|
//[TOC] [\tableofcontents]
|
|
|
|
[1] ImExAlgebra
|
|
|
|
This algebra provides import export functions for different
|
|
data formats.
|
|
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
[TOC]
|
|
|
|
1 Overview
|
|
|
|
This file contains the implementation import / export operators.
|
|
|
|
2 Defines, includes, and constants
|
|
|
|
*/
|
|
|
|
|
|
#include <cmath>
|
|
#include <stack>
|
|
#include <limits>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <fstream>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
#include <iostream>
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
#include "SocketIO.h"
|
|
|
|
#include "NestedList.h"
|
|
#include "QueryProcessor.h"
|
|
#include "AlgebraManager.h"
|
|
#include "Algebra.h"
|
|
#include "StandardTypes.h"
|
|
#include "Algebras/Relation-C++/RelationAlgebra.h"
|
|
#include "SecondoSystem.h"
|
|
#include "Algebras/FText/FTextAlgebra.h"
|
|
#include "Algebras/Spatial/SpatialAlgebra.h"
|
|
#include "DateTime.h"
|
|
#include "Algebras/TopOps/TopOpsAlgebra.h"
|
|
#include "Algebras/BinaryFile/BinaryFileAlgebra.h"
|
|
#include "Tools/Flob/DbArray.h"
|
|
#include "Symbols.h"
|
|
#include "FileSystem.h"
|
|
#include "ListUtils.h"
|
|
|
|
#include "version.h"
|
|
#include "DbVersion.h"
|
|
#include "Algebras/Spatial/RegionTools.h"
|
|
#include "NMEAImporter.h"
|
|
#include "Stream.h"
|
|
#include "aisdecode.h"
|
|
#include "Base64.h"
|
|
#include "WinUnix.h"
|
|
|
|
|
|
extern NestedList* nl;
|
|
extern QueryProcessor* qp;
|
|
extern AlgebraManager* am;
|
|
|
|
using namespace std;
|
|
|
|
|
|
#define FILE_BUFFER_SIZE 1048576
|
|
|
|
|
|
uint32_t readBigInt32(ifstream& file){
|
|
uint32_t res;
|
|
file.read(reinterpret_cast<char*>(&res),4);
|
|
if(WinUnix::isLittleEndian()){
|
|
res = WinUnix::convertEndian(res);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
uint32_t readLittleInt32(ifstream& file){
|
|
uint32_t res;
|
|
file.read(reinterpret_cast<char*>(&res),4);
|
|
if(!WinUnix::isLittleEndian()){
|
|
res = WinUnix::convertEndian(res);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
uint16_t readLittleInt16(ifstream& file){
|
|
uint16_t res;
|
|
file.read(reinterpret_cast<char*>(&res),2);
|
|
if(!WinUnix::isLittleEndian()){
|
|
res = WinUnix::convertEndian(res);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
double readLittleDouble(ifstream& file){
|
|
uint64_t tmp;
|
|
file.read(reinterpret_cast<char*>(&tmp),8);
|
|
if(!WinUnix::isLittleEndian()){
|
|
tmp = WinUnix::convertEndian(tmp);
|
|
}
|
|
void* tmpv = (void*) &tmp;
|
|
double res = * (reinterpret_cast<double*>(tmpv));
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
1 Operator ~csvexport~
|
|
|
|
1.1 Type Mapping
|
|
|
|
stream(CSVEXPORTABLE) x text x bool -> stream(CSVEXPORTABLE)
|
|
stream ( tuple ( (a1 t1) ... (an tn))) x string x
|
|
bool x bool -> stream (tuple(...))
|
|
stream ( tuple ( (a1 t1) ... (an tn))) x string x bool x
|
|
bool x string -> stream (tuple(...))
|
|
|
|
*/
|
|
|
|
bool cvsexportableAttrList(ListExpr attrList, ListExpr errorInfo){
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr attr = nl->First(attrList);
|
|
attrList = nl->Rest(attrList);
|
|
ListExpr atype = nl->Second(attr);
|
|
if(!am->CheckKind(Kind::CSVEXPORTABLE(), atype,errorInfo)){
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ListExpr csvexportTM(ListExpr args){
|
|
int len = nl->ListLength(args);
|
|
if(len != 3 && len != 4 && len!=5){
|
|
ErrorReporter::ReportError("wrong number of arguments");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr errorInfo = nl->OneElemList(nl->SymbolAtom("ERROR"));
|
|
if(len == 3){ // stream(CSV) x string x bool
|
|
if(!nl->IsEqual(nl->Second(args),FText::BasicType()) ||
|
|
!nl->IsEqual(nl->Third(args),CcBool::BasicType())){
|
|
ErrorReporter::ReportError("stream x text x bool expected");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
if(nl->ListLength(stream)!=2){
|
|
ErrorReporter::ReportError("stream x text x bool expected");
|
|
return nl->TypeError();
|
|
}
|
|
if(!nl->IsEqual(nl->First(stream),Symbol::STREAM())){
|
|
ErrorReporter::ReportError("stream x text x bool expected");
|
|
return nl->TypeError();
|
|
}
|
|
if(!am->CheckKind(Kind::CSVEXPORTABLE(),nl->Second(stream),errorInfo)){
|
|
if(listutils::isTupleStream(stream)){
|
|
ListExpr attrList = nl->Second(nl->Second(stream));
|
|
if(!cvsexportableAttrList(attrList,errorInfo)){
|
|
return listutils::typeError("at least on attribute in tuple "
|
|
"is not csvexportable");
|
|
}
|
|
return stream;
|
|
} else {
|
|
return listutils::typeError("stream element not in "
|
|
"kind csvexportable");
|
|
}
|
|
}
|
|
return stream;
|
|
} else { // stream(tuple(...) )
|
|
if( !nl->IsEqual(nl->Second(args),FText::BasicType()) ||
|
|
!nl->IsEqual(nl->Third(args),CcBool::BasicType()) ||
|
|
!nl->IsEqual(nl->Fourth(args),CcBool::BasicType()) ){
|
|
ErrorReporter::ReportError("stream x text x bool x"
|
|
" bool [x string] expected");
|
|
return nl->TypeError();
|
|
}
|
|
if(len==5 && !nl->IsEqual(nl->Fifth(args),CcString::BasicType())){
|
|
ErrorReporter::ReportError("stream x text x bool x "
|
|
"bool [x string] expected");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
if(!Stream<Tuple>::checkType(stream)){
|
|
return listutils::typeError("first argument does is not "
|
|
"a CSV exportable tuple stream");
|
|
}
|
|
ListExpr attrList = nl->Second(nl->Second(stream));
|
|
// check attrList
|
|
if(!cvsexportableAttrList(attrList, errorInfo)){
|
|
return listutils::typeError("at least one attribute is not "
|
|
"in kind CSVEXPORTABLE");
|
|
}
|
|
return stream;
|
|
}
|
|
}
|
|
|
|
/*
|
|
1.2 Value Mappings for csvexport
|
|
|
|
*/
|
|
|
|
class CsvExportLocalInfo {
|
|
|
|
public:
|
|
|
|
CsvExportLocalInfo(string fname, bool append){
|
|
if(append){
|
|
f.open(fname.c_str(), ios::out | ios::app);
|
|
} else {
|
|
f.open(fname.c_str(),ios::out | ios::trunc);
|
|
}
|
|
}
|
|
|
|
CsvExportLocalInfo(string fname, bool append,bool names,
|
|
const string& sep,
|
|
ListExpr type){
|
|
this->sep = sep;
|
|
if(append){
|
|
f.open(fname.c_str(), ios::out | ios::app);
|
|
} else {
|
|
f.open(fname.c_str(),ios::out | ios::trunc);
|
|
}
|
|
if(names){
|
|
ListExpr rest = nl->Second(nl->Second(type));
|
|
ListExpr first = nl->First(rest);
|
|
rest = nl->Rest(rest);
|
|
f << nl->SymbolValue(nl->First(first));
|
|
while(!nl->IsEmpty(rest)){
|
|
f << ",";
|
|
first = nl->First(rest);
|
|
rest = nl->Rest(rest);
|
|
f << nl->SymbolValue(nl->First(first));
|
|
}
|
|
f << endl;
|
|
}
|
|
}
|
|
|
|
~CsvExportLocalInfo(){
|
|
f.close();
|
|
}
|
|
|
|
bool isOk(){
|
|
return f.good();
|
|
}
|
|
|
|
void write(Attribute* attr){
|
|
f << attr->getCsvStr() << endl;
|
|
}
|
|
|
|
void write(Tuple* tuple){
|
|
int s = tuple->GetNoAttributes();
|
|
for(int i=0;i<s;i++){
|
|
if(i>0){
|
|
f << sep;
|
|
}
|
|
f << tuple->GetAttribute(i)->getCsvStr();
|
|
}
|
|
f << endl;
|
|
}
|
|
|
|
private:
|
|
fstream f;
|
|
string sep;
|
|
};
|
|
|
|
int CsvExportVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s)
|
|
{
|
|
switch( message )
|
|
{
|
|
case OPEN:{
|
|
qp->Open(args[0].addr);
|
|
FText* fname = static_cast<FText*>(args[1].addr);
|
|
CcBool* append = static_cast<CcBool*>(args[2].addr);
|
|
if(!fname->IsDefined() || !append->IsDefined()){
|
|
local.setAddr(0);
|
|
} else {
|
|
CsvExportLocalInfo* linfo;
|
|
linfo = (new CsvExportLocalInfo(fname->GetValue(),
|
|
append->GetBoolval()));
|
|
if(!linfo->isOk()){
|
|
delete linfo;
|
|
local.setAddr(0);
|
|
} else {
|
|
local.setAddr(linfo);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST:{
|
|
if(local.addr==0){
|
|
return CANCEL;
|
|
} else {
|
|
Word elem;
|
|
qp->Request(args[0].addr,elem);
|
|
if(qp->Received(args[0].addr)){
|
|
CsvExportLocalInfo* linfo;
|
|
linfo = static_cast<CsvExportLocalInfo*>(local.addr);
|
|
linfo->write( static_cast<Attribute*>( elem.addr));
|
|
result = elem;
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
}
|
|
}
|
|
|
|
case CLOSE:{
|
|
qp->Close(args[0].addr);
|
|
if(local.addr){
|
|
CsvExportLocalInfo* linfo;
|
|
linfo = static_cast<CsvExportLocalInfo*>(local.addr);
|
|
delete linfo;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int CsvExportVM2(Word* args, Word& result,
|
|
int message, Word& local, Supplier s)
|
|
{
|
|
switch( message )
|
|
{
|
|
case OPEN:{
|
|
qp->Open(args[0].addr);
|
|
FText* fname = static_cast<FText*>(args[1].addr);
|
|
CcBool* append = static_cast<CcBool*>(args[2].addr);
|
|
bool names = false;
|
|
if(qp->GetNoSons(s)>3){
|
|
CcBool* snames = static_cast<CcBool*>(args[3].addr);
|
|
if(snames->IsDefined()){
|
|
names = snames->GetValue();
|
|
}
|
|
}
|
|
string sep = ",";
|
|
if(qp->GetNoSons(s)==5){
|
|
CcString* ccSep = static_cast<CcString*>(args[4].addr);
|
|
sep = ccSep->GetValue();
|
|
if(sep.length()==0){
|
|
sep =",";
|
|
}
|
|
}
|
|
if(!fname->IsDefined() || !append->IsDefined() ){
|
|
local.setAddr(0);
|
|
} else {
|
|
CsvExportLocalInfo* linfo;
|
|
linfo = (new CsvExportLocalInfo(fname->GetValue(),
|
|
append->GetBoolval(),
|
|
names,
|
|
sep,
|
|
qp->GetType(s)));
|
|
if(!linfo->isOk()){
|
|
delete linfo;
|
|
local.setAddr(0);
|
|
} else {
|
|
local.setAddr(linfo);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST:{
|
|
if(local.addr==0){
|
|
return CANCEL;
|
|
} else {
|
|
Word elem;
|
|
qp->Request(args[0].addr,elem);
|
|
if(qp->Received(args[0].addr)){
|
|
CsvExportLocalInfo* linfo;
|
|
linfo = static_cast<CsvExportLocalInfo*>(local.addr);
|
|
linfo->write( static_cast<Tuple*>( elem.addr));
|
|
result = elem;
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
}
|
|
}
|
|
|
|
case CLOSE:{
|
|
qp->Close(args[0].addr);
|
|
if(local.addr){
|
|
CsvExportLocalInfo* linfo;
|
|
linfo = static_cast<CsvExportLocalInfo*>(local.addr);
|
|
delete linfo;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping csvexportmap[] =
|
|
{ CsvExportVM, CsvExportVM2 };
|
|
|
|
int csvExportSelect( ListExpr args )
|
|
{
|
|
return Stream<Tuple>::checkType(nl->First(args))?1:0;
|
|
}
|
|
|
|
/*
|
|
1.3 Specification
|
|
|
|
*/
|
|
|
|
|
|
const string CsvExportSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(X) x text x bool -> stream(X)\n"
|
|
"stream(tuple(...))) x text x bool x bool "
|
|
"[x string]-> stream(tuple...)</text--->"
|
|
"<text> stream csvexport [ file , append, "
|
|
"[writenames, [separator]]]</text--->"
|
|
"<text> Exports stream content to a csv file </text--->"
|
|
"<text>query ten feed exportcsv['ten.csv', "
|
|
"FALSE,TRUE,\";\"] count</text--->"
|
|
") )";
|
|
|
|
/*
|
|
1.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator csvexport( "csvexport",
|
|
CsvExportSpec,
|
|
2,
|
|
csvexportmap,
|
|
csvExportSelect,
|
|
csvexportTM);
|
|
|
|
|
|
|
|
/*
|
|
1 CSV Import
|
|
|
|
1.1 Type Mapping
|
|
|
|
rel(tuple(a[_]1 : t[_]1)...(a[_]n : t[_]n)) x text x
|
|
int x string [x string] [x bool] -> stream(tuple(...))
|
|
|
|
where t[_]i is CSVIMPORTABLE.
|
|
|
|
text : indicates the filename containing the csv data
|
|
int : number of lines to ignore (header)
|
|
string : ignore Lines starting with this string (comment)
|
|
use an empty string for disable this feature
|
|
|
|
optional string : use another separator, default is ","
|
|
optional bool : if set to true, ignore separators within quotes,
|
|
for example,
|
|
treat "hello, hello" as a single item , default is false
|
|
optional bool : multiline. If set to true, values within quotes can contain
|
|
linebreaks
|
|
|
|
*/
|
|
|
|
ListExpr csvimportTM(ListExpr args){
|
|
string err = " rel(tuple(a_1 : t_1)...(a_n : t_n)) x "
|
|
" text x int x string [x string [x bool [ x bool]]] expected";
|
|
|
|
int len = nl->ListLength(args);
|
|
if((len!=4) && (len!=5) && (len !=6) && (len !=7)){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
if(!Relation::checkType(nl->First(args)) ||
|
|
( !FText::checkType(nl->Second(args) )
|
|
&& !BinaryFile::checkType(nl->Second(args))) ||
|
|
!CcInt::checkType(nl->Third(args)) ||
|
|
!CcString::checkType(nl->Fourth(args) )){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
// check attribute types for csv importable
|
|
ListExpr errorInfo = nl->OneElemList(nl->SymbolAtom("ERROR"));
|
|
|
|
ListExpr attrlist = nl->Second(nl->Second(nl->First(args)));
|
|
if(nl->IsEmpty(attrlist)){
|
|
return listutils::typeError(err);
|
|
}
|
|
while(!nl->IsEmpty(attrlist)){
|
|
ListExpr type = nl->Second(nl->First(attrlist));
|
|
if(!am->CheckKind(Kind::CSVIMPORTABLE(),type,errorInfo)){
|
|
ErrorReporter::ReportError("attribute type '" + nl->ToString(type) +
|
|
"' not in kind CSVIMPORTABLE");
|
|
return nl->TypeError();
|
|
}
|
|
attrlist = nl->Rest(attrlist);
|
|
}
|
|
|
|
// create result list
|
|
ListExpr resList = nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->Second(nl->First(args)));
|
|
|
|
|
|
|
|
if(len==4){ // append two default values
|
|
ListExpr defaults = nl->ThreeElemList(
|
|
nl->StringAtom(","),
|
|
nl->BoolAtom(false),
|
|
nl->BoolAtom(false)
|
|
);
|
|
return nl->ThreeElemList( nl->SymbolAtom(Symbols::APPEND()),
|
|
defaults, resList);
|
|
}
|
|
|
|
|
|
// the fifth element must be of type string
|
|
if(!CcString::checkType(nl->Fifth(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
if(len==5){ // set the optional booleans to be false
|
|
ListExpr defaults = nl->TwoElemList(nl->BoolAtom(false),
|
|
nl->BoolAtom(false));
|
|
|
|
return nl->ThreeElemList( nl->SymbolAtom(Symbols::APPEND()),
|
|
defaults, resList);
|
|
}
|
|
|
|
if(len==6){
|
|
if(!CcBool::checkType(nl->Sixth(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr defaults = nl->OneElemList(nl->BoolAtom(false));
|
|
return nl->ThreeElemList( nl->SymbolAtom(Symbols::APPEND()),
|
|
defaults, resList);
|
|
}
|
|
|
|
// len = 7, no defaults
|
|
if(!CcBool::checkType(nl->Seventh(args))){
|
|
return listutils::typeError("7th attribute must be of type bool");
|
|
}
|
|
|
|
return resList;
|
|
}
|
|
|
|
|
|
/*
|
|
1.2 Specification
|
|
|
|
|
|
*/
|
|
|
|
|
|
const string csvimportSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>rel(tuple(...) x text x int x string [x string [x bool]] "
|
|
"-> stream (tuple(...))</text--->"
|
|
"<text>Rel csvimport[ FileName, HeaderSize, Comment, Separator, "
|
|
"quotes, multiline]"
|
|
" </text--->"
|
|
"<text> Returns the content of the CSV (Comma Separated Value) file "
|
|
"'FileName' as a stream of tuples. The types are defined by the first "
|
|
"argument, 'Rel', whose tuple type acts as a template for the result tuple "
|
|
"type. 'HeaderSize' specifies an amount of lines to ignore at the head of "
|
|
"the imported textfile. 'Comment' defines a character that marks comment "
|
|
"lines within the textfile. 'Separator' defines the character that is "
|
|
"expected to separate the fields within each line of the textfile (default "
|
|
"is \",\"."
|
|
"quotes means that separators within quotes are ignored. If multiline is set"
|
|
"to true, attributes within quotes can span more than 1 line. "
|
|
"</text--->"
|
|
"<text> not tested !!!</text--->"
|
|
") )";
|
|
|
|
|
|
/*
|
|
1.3 Abstract class for parsing csv input streams
|
|
|
|
The methods open(), close(), readNextData()
|
|
and isDataAvailable() must be implemented
|
|
in subclasses
|
|
|
|
*/
|
|
|
|
class CSVInputStream {
|
|
|
|
// Activate debug messages
|
|
//#define _CSVInputStream_DEBUG
|
|
|
|
enum parsermode { UNQUOTED_ENVIRONMENT, QUOTED_ENVIRONMENT };
|
|
|
|
public:
|
|
CSVInputStream() { }
|
|
|
|
/*
|
|
1.3 Create a new CSVInputStream. The 1st parameter is the
|
|
source to read, the 2nd parameter is the separator character,
|
|
the 3rd parameter is the comment character
|
|
|
|
*/
|
|
CSVInputStream(string* source, string* separator, string* comment) {
|
|
if(source){
|
|
this -> source = string(*source);
|
|
} else {
|
|
this->source = "";
|
|
}
|
|
this -> separator = string(*separator);
|
|
this -> comment = string(*comment);
|
|
|
|
bufferPos = sizeof(buffer);
|
|
bufferSize = sizeof(buffer);
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
}
|
|
|
|
|
|
virtual ~CSVInputStream() {
|
|
close();
|
|
}
|
|
|
|
// Template methods, sould be overwritten in subclasses
|
|
virtual void open() {};
|
|
virtual void close() {};
|
|
|
|
// callback after a completed tuple
|
|
virtual void tupleComplete() {
|
|
if(buffer[bufferPos-1] != '\n'){
|
|
skipLine();
|
|
}
|
|
}
|
|
|
|
// is unprocessed data available
|
|
virtual bool isDataAvailable() { return false; }
|
|
|
|
// Read new unprocessed data into the buffer
|
|
virtual size_t readNextData
|
|
(char *buffer, size_t start, size_t maxBytes) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Are unprocessed bytes in the buffer or new data
|
|
// available?
|
|
bool isUnprocessedDataAvailable() {
|
|
return ((bufferSize != bufferPos) || isDataAvailable());
|
|
}
|
|
|
|
// Is the multiline support active?
|
|
bool getMultiline() {
|
|
return multiline;
|
|
}
|
|
|
|
// set multiline support
|
|
void setMultiline(bool myMultiline) {
|
|
multiline = myMultiline;
|
|
}
|
|
|
|
// Is the input quoted?
|
|
bool getQuotes() {
|
|
return quotes;
|
|
}
|
|
|
|
// set quoting support
|
|
void setQuotes(bool myQuotes) {
|
|
quotes = myQuotes;
|
|
}
|
|
|
|
// get the configured separator symbol
|
|
string getSeparator() {
|
|
return separator;
|
|
}
|
|
|
|
/*
|
|
1.3.1 Fill buffer with new data
|
|
|
|
*/
|
|
size_t fillBuffer() {
|
|
|
|
// copy unprocessed data to the beginning of the array
|
|
size_t remainingBytes = bufferSize - bufferPos;
|
|
if(remainingBytes > 0) {
|
|
memcpy(buffer, buffer+bufferPos, remainingBytes);
|
|
}
|
|
|
|
#ifdef _CSVInputStream_DEBUG
|
|
cout << "fillBuffer() : Copied " << remainingBytes;
|
|
cout << " bytes to the beginning of the buffer" << endl;
|
|
#endif
|
|
|
|
bufferSize = remainingBytes;
|
|
bufferPos = 0;
|
|
|
|
// Fill buffer
|
|
if(isDataAvailable()) {
|
|
|
|
size_t readData = readNextData(buffer, bufferPos,
|
|
sizeof(buffer) - bufferSize );
|
|
|
|
bufferSize = bufferSize + readData;
|
|
|
|
#ifdef _CSVInputStream_DEBUG
|
|
cout << "fillBuffer() Filled " << readData << endl;
|
|
cout << "Buffersize is now: " << bufferSize << " bufferPos ";
|
|
cout << bufferPos << endl;
|
|
#endif
|
|
|
|
return readData;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
1.3.2 Skip all bytes until we reach the line end
|
|
|
|
*/
|
|
bool skipLine() {
|
|
|
|
// Process bytes
|
|
while(true) {
|
|
|
|
// Are all Bytes inside our buffer processed?
|
|
if(bufferPos == bufferSize){
|
|
|
|
// No new bytes available?
|
|
if(fillBuffer() == 0){
|
|
return false;
|
|
}
|
|
}
|
|
|
|
char c = buffer[bufferPos];
|
|
bufferPos++;
|
|
|
|
if(c == '\n') {
|
|
#ifdef _CSVInputStream_DEBUG
|
|
cout << "Skipped one line" << endl;
|
|
cout << "BufferPos is " << bufferPos << endl;
|
|
cout << "BufferSize is " << bufferSize << endl;
|
|
#endif
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
1.3.3 Get next csv field
|
|
|
|
*/
|
|
string getNextValue(bool& eol) {
|
|
|
|
parsermode mode = UNQUOTED_ENVIRONMENT;
|
|
|
|
string result;
|
|
|
|
bool done = false;
|
|
eol = false;
|
|
// Process bytes
|
|
while(! done) {
|
|
|
|
// Are all Bytes inside our buffer processed?
|
|
if(bufferPos == bufferSize){
|
|
// No new bytes available?
|
|
if(! isDataAvailable() || fillBuffer() == 0){
|
|
done = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
char c = buffer[bufferPos];
|
|
bufferPos++;
|
|
|
|
// normal mode (unquoted environment)
|
|
if(mode == UNQUOTED_ENVIRONMENT) {
|
|
|
|
// If this is the first byte of our field
|
|
// and it is a comment char - skip the whole line
|
|
if((result.empty()) && (comment.size() > 0)
|
|
&& (comment.find(c) != string::npos)) {
|
|
|
|
skipLine();
|
|
continue;
|
|
}
|
|
|
|
// If c is a quote char and we are handling
|
|
// quote chars
|
|
if (c == '"' && quotes) {
|
|
// switch into quoted environment
|
|
// and skip quote char
|
|
mode = QUOTED_ENVIRONMENT;
|
|
continue;
|
|
}
|
|
|
|
// Field separator or newline read?
|
|
if ((separator.find(c)!=string::npos) || (c == '\n')) {
|
|
done = true;
|
|
eol = c == '\n';
|
|
} else {
|
|
result += c;
|
|
}
|
|
|
|
} else {
|
|
|
|
// We are inside a quoted environment
|
|
// switch back to the normal mode
|
|
// as soon as we read the next " char
|
|
if (c == '"') {
|
|
mode = UNQUOTED_ENVIRONMENT;
|
|
continue;
|
|
}
|
|
|
|
// Handling of newline chars
|
|
if(c == '\n') {
|
|
|
|
// Append newline chars to output?
|
|
if(! multiline) {
|
|
// We are not in multiline mode,
|
|
// skip newline char
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Append char to output
|
|
result += c;
|
|
}
|
|
}
|
|
|
|
#ifdef _CSVInputStream_DEBUG
|
|
cout << "Return: " << ss.str() << endl;
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
protected:
|
|
bool multiline; // process multiline fields
|
|
bool quotes; // use quotes
|
|
string separator; // our separator (e.g. , or ;)
|
|
string comment; // comment (e.g. //)
|
|
string source; // name of the source
|
|
char buffer[102400]; // buffer (100 KB)
|
|
size_t bufferPos; // position of next unprocessed char
|
|
size_t bufferSize; // last position of unprocessed chars in buffer
|
|
};
|
|
|
|
|
|
/*
|
|
1.4 Class for processing file csv input streams
|
|
|
|
*/
|
|
class CSVFileInputStream : public CSVInputStream {
|
|
|
|
public:
|
|
CSVFileInputStream(string *mySource, string *mySeparator,
|
|
string *myComment)
|
|
: CSVInputStream(mySource, mySeparator, myComment) {
|
|
|
|
file = NULL;
|
|
}
|
|
|
|
~CSVFileInputStream() {
|
|
close();
|
|
}
|
|
|
|
virtual void open() {
|
|
// Is file accessible?
|
|
if( access( source.c_str(), R_OK ) != 0 ) {
|
|
cout << endl;
|
|
cout << "Error: Unable to open file: " << source << endl;
|
|
cout << endl;
|
|
} else {
|
|
file = fopen(source.c_str(), "rb");
|
|
}
|
|
}
|
|
|
|
virtual void close() {
|
|
if(file != NULL) {
|
|
fclose(file);
|
|
file = NULL;
|
|
}
|
|
}
|
|
|
|
virtual bool isDataAvailable() {
|
|
// Is file open?
|
|
if(file == NULL) {
|
|
return false;
|
|
}
|
|
|
|
return ! feof(file);
|
|
}
|
|
|
|
virtual size_t readNextData(char *buffer, size_t start,
|
|
size_t maxBytes) {
|
|
|
|
if(! isDataAvailable() ) {
|
|
return 0;
|
|
}
|
|
|
|
size_t readBytes = fread(buffer + start, 1, maxBytes, file);
|
|
|
|
#ifdef _CSVInputStream_DEBUG
|
|
cout << "readNextDataCalled read: " << readBytes;
|
|
cout << " eof " << feof(file) << endl;
|
|
#endif
|
|
|
|
return readBytes;
|
|
}
|
|
|
|
private:
|
|
FILE* file;
|
|
|
|
};
|
|
|
|
|
|
/*
|
|
1.4 Class for processing binary file csv input streams
|
|
|
|
*/
|
|
class CSVBinFileInputStream : public CSVInputStream {
|
|
|
|
public:
|
|
CSVBinFileInputStream(BinaryFile *source, string *mySeparator,
|
|
string *myComment)
|
|
: CSVInputStream(0, mySeparator, myComment) {
|
|
size = source->GetSize();
|
|
this->source = source;
|
|
pos = 0;
|
|
}
|
|
|
|
~CSVBinFileInputStream() {
|
|
}
|
|
|
|
virtual void open() {
|
|
// we cna just read binary files
|
|
}
|
|
|
|
virtual void close() {
|
|
// we don't have to close binary files
|
|
}
|
|
|
|
virtual bool isDataAvailable() {
|
|
return pos < size;
|
|
}
|
|
|
|
virtual size_t readNextData(char *buffer, size_t start,
|
|
size_t maxBytes) {
|
|
|
|
if(! isDataAvailable() ) {
|
|
return 0;
|
|
}
|
|
size_t abytes = size-pos;
|
|
size_t readBytes = min(abytes,maxBytes);
|
|
source->Get(pos,readBytes,buffer+start);
|
|
pos += readBytes;
|
|
return readBytes;
|
|
}
|
|
|
|
private:
|
|
BinaryFile* source;
|
|
size_t pos;
|
|
size_t size;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
1.5 Class for processing network csv input streams
|
|
|
|
*/
|
|
class CSVNetworkInputStream : public CSVInputStream {
|
|
|
|
public:
|
|
CSVNetworkInputStream(string *mySource, string *mySeparator,
|
|
string *myComment)
|
|
: CSVInputStream(mySource, mySeparator, myComment) {
|
|
|
|
readSocket = NULL;
|
|
listenSocket = NULL;
|
|
}
|
|
|
|
virtual string extractPortFromSource() {
|
|
|
|
// tcp:// - 6 chars
|
|
string stringport = source.substr(6);
|
|
return stringport;
|
|
}
|
|
|
|
virtual void open() {
|
|
string stringport = extractPortFromSource();
|
|
|
|
cout << "Open TCP Port: " << stringport << endl;
|
|
|
|
listenSocket = Socket::CreateGlobal("0.0.0.0", stringport.c_str());
|
|
|
|
if (!listenSocket || !listenSocket->IsOk()) {
|
|
cerr << "unable listening to port: " << stringport.c_str();
|
|
return;
|
|
}
|
|
|
|
#if defined(unix) || defined(__unix__) || defined(__unix)
|
|
// Set socket option "reuse"
|
|
// Prevents bind errors if the query is
|
|
// executed multiple times
|
|
int yes = 1;
|
|
if (setsockopt(listenSocket -> GetDescriptor(),
|
|
SOL_SOCKET, SO_REUSEADDR, &yes,
|
|
sizeof(int)) == -1 ) {
|
|
|
|
cerr << "Set socket options failed" << endl;
|
|
}
|
|
#endif
|
|
|
|
readSocket = listenSocket->Accept();
|
|
|
|
if(! readSocket && ! readSocket -> IsOk()) {
|
|
cerr << "Accept on socket failed" << endl;
|
|
return;
|
|
}
|
|
|
|
// Close server socket
|
|
listenSocket -> Close();
|
|
|
|
}
|
|
|
|
virtual size_t readNextData(char *buffer,
|
|
size_t start, size_t maxBytes) {
|
|
|
|
if(! isDataAvailable() ) {
|
|
return 0;
|
|
}
|
|
|
|
#ifdef _CSVInputStream_DEBUG
|
|
cout << "readNextData() start " << start << endl;
|
|
#endif
|
|
|
|
// Read data (socket is set to blocking mode)
|
|
int readBytes = readSocket -> Read(buffer + start, 1, maxBytes);
|
|
|
|
#ifdef _CSVInputStream_DEBUG
|
|
cout << "readNextDataCalled read (from socket): " << readBytes << endl;
|
|
#endif
|
|
|
|
// Check for last read byte is
|
|
// EOT (End of Transmission / ASCII 004)
|
|
if(*(buffer + start + readBytes -1) == '\004') {
|
|
readBytes--;
|
|
close();
|
|
} else if(readBytes <= 0) {
|
|
|
|
#ifdef _CSVInputStream_DEBUG
|
|
cout << "Read 0 bytes from socket in blocking mode ";
|
|
cout << ", closing socket" << endl;
|
|
#endif
|
|
|
|
close();
|
|
return 0;
|
|
}
|
|
|
|
return readBytes;
|
|
}
|
|
|
|
virtual void close() {
|
|
#ifdef _CSVInputStream_DEBUG
|
|
cout << "Closing TCP Port: " << source << endl;
|
|
#endif
|
|
|
|
if(readSocket != NULL) {
|
|
readSocket -> Close();
|
|
delete readSocket;
|
|
readSocket = NULL;
|
|
}
|
|
|
|
if(listenSocket != NULL) {
|
|
listenSocket -> Close();
|
|
delete listenSocket;
|
|
listenSocket = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
1.3 Is unprocessed data available?
|
|
|
|
*/
|
|
virtual bool isDataAvailable() {
|
|
|
|
// Client is connected
|
|
if(readSocket != NULL) {
|
|
return true;
|
|
}
|
|
|
|
// There are unprocessed bytes in the buffer
|
|
if(bufferPos != bufferSize) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
Socket* listenSocket; // FD for server listen
|
|
Socket* readSocket; // FD for client handling
|
|
struct sockaddr_in serv_addr; // Server address
|
|
struct sockaddr_in client_addr; // Client address
|
|
|
|
};
|
|
|
|
/*
|
|
1.6 Class for processing reliable network csv input streams
|
|
|
|
*/
|
|
class CSVReliableNetworkInputStream : public CSVNetworkInputStream {
|
|
|
|
public:
|
|
CSVReliableNetworkInputStream(string *mySource,
|
|
string *mySeparator, string *myComment)
|
|
: CSVNetworkInputStream(mySource, mySeparator, myComment) {
|
|
|
|
unacknowledgedTuples = 0;
|
|
acknowledgeAfter = 1;
|
|
}
|
|
|
|
/*
|
|
1.6.1 process source url
|
|
"tcplb://port/acknowledgeAfter"
|
|
|
|
*/
|
|
virtual string extractPortFromSource() {
|
|
|
|
// tcplb:// - 8 chars
|
|
string stringport = source.substr(8);
|
|
|
|
size_t pos = stringport.find("/");
|
|
|
|
// Process optional acknowledgeAfter field
|
|
if(pos != string::npos) {
|
|
|
|
string acknowledgeAfterString
|
|
= stringport.substr(pos + 1, stringport.length());
|
|
acknowledgeAfter = atoi(acknowledgeAfterString.c_str());
|
|
|
|
stringport = stringport.substr(0, pos);
|
|
}
|
|
|
|
return stringport;
|
|
}
|
|
|
|
|
|
/*
|
|
1.6.2 send an ack to server after
|
|
every ~acknowledgeAfter~ tuples
|
|
|
|
*/
|
|
virtual void tupleComplete() {
|
|
|
|
unacknowledgedTuples++;
|
|
|
|
if(unacknowledgedTuples >= acknowledgeAfter) {
|
|
// Send an ack char to the server
|
|
readSocket -> Write("\006", sizeof(char));
|
|
unacknowledgedTuples = 0;
|
|
}
|
|
}
|
|
|
|
protected:
|
|
size_t unacknowledgedTuples; // Number of
|
|
// unacknowledged Tuples
|
|
|
|
size_t acknowledgeAfter; // Send an ack char to server
|
|
// after every n tuples
|
|
};
|
|
|
|
/*
|
|
1.7 This is a factory class for CSVInportStream Objects
|
|
|
|
*/
|
|
class CSVInputStreamFactory {
|
|
|
|
public:
|
|
static CSVInputStream* getStreamForInput(string* input,
|
|
string* separator, string* comment) {
|
|
|
|
// Create a network intance
|
|
if(input -> compare(0, 6, "tcp://") == 0) {
|
|
return new CSVNetworkInputStream
|
|
(input, separator, comment);
|
|
}
|
|
|
|
// Create a reliable network instance
|
|
if(input -> compare(0, 8, "tcplb://") == 0) {
|
|
return new CSVReliableNetworkInputStream
|
|
(input, separator, comment);
|
|
}
|
|
|
|
// Default: Create file instance
|
|
return new CSVFileInputStream(input, separator, comment);
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
1.3 Value Mapping for csvimport
|
|
|
|
*/
|
|
class CsvImportInfo{
|
|
public:
|
|
CsvImportInfo(ListExpr type, FText* filename,
|
|
CcInt* hSize , CcString* comment,
|
|
CcString* separator,
|
|
CcBool* quotes,
|
|
CcBool* multiline = 0
|
|
){
|
|
|
|
this->separator = ",";
|
|
this->quotes = false;
|
|
|
|
if(separator->IsDefined() ){
|
|
string sep = separator->GetValue();
|
|
sep = stringutils::replaceAll(sep,"\\t","\t");
|
|
if(sep.length()>0){
|
|
this->separator=sep;
|
|
}
|
|
}
|
|
if(quotes->IsDefined()){
|
|
this->quotes = quotes->GetValue();
|
|
}
|
|
|
|
BasicTuple = 0;
|
|
tupleType = 0;
|
|
defined = filename->IsDefined() && hSize->IsDefined() &&
|
|
comment->IsDefined();
|
|
if(!defined){
|
|
return;
|
|
}
|
|
string name = filename->GetValue();
|
|
this->multiLine = false;
|
|
if(multiline && multiline->IsDefined()){
|
|
this->multiLine = multiline->GetBoolval();
|
|
}
|
|
|
|
|
|
this->comment = comment->GetValue();
|
|
useComment = this->comment.size()>0;
|
|
|
|
// Create csvinputstream
|
|
csvinputstream = CSVInputStreamFactory::getStreamForInput(&name,
|
|
&this->separator, &this->comment);
|
|
|
|
csvinputstream -> setMultiline(multiline);
|
|
csvinputstream -> setQuotes( quotes -> GetBoolval( ));
|
|
|
|
csvinputstream -> open();
|
|
|
|
if(! csvinputstream -> isUnprocessedDataAvailable() ){
|
|
defined = false;
|
|
return;
|
|
}
|
|
|
|
// skip header
|
|
int skip = hSize->GetIntval();
|
|
|
|
for(int i=0;i<skip && csvinputstream -> isUnprocessedDataAvailable();
|
|
i++) {
|
|
|
|
csvinputstream -> skipLine();
|
|
// Callback (needed for tcplb protocol)
|
|
csvinputstream -> tupleComplete();
|
|
}
|
|
|
|
if(!csvinputstream -> isUnprocessedDataAvailable()){
|
|
defined=false;
|
|
return;
|
|
}
|
|
|
|
ListExpr numType = nl->Second(
|
|
SecondoSystem::GetCatalog()->NumericType((type)));
|
|
tupleType = new TupleType(numType);
|
|
BasicTuple = new Tuple(tupleType);
|
|
// build instances for each type
|
|
ListExpr attrList = nl->Second(nl->Second(type));
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr attrType = nl->Second(nl->First(attrList));
|
|
attrList = nl->Rest(attrList);
|
|
int algId;
|
|
int typeId;
|
|
string tname;
|
|
if(! ((SecondoSystem::GetCatalog())->LookUpTypeExpr(attrType,
|
|
tname, algId, typeId))){
|
|
defined = false;
|
|
return;
|
|
}
|
|
Word w = am->CreateObj(algId,typeId)(attrType);
|
|
instances.push_back(static_cast<Attribute*>(w.addr));
|
|
}
|
|
}
|
|
|
|
|
|
CsvImportInfo(ListExpr type, BinaryFile* file,
|
|
CcInt* hSize , CcString* comment,
|
|
CcString* separator,
|
|
CcBool* quotes,
|
|
CcBool* multiline = 0
|
|
){
|
|
|
|
this->separator = ",";
|
|
this->quotes = false;
|
|
|
|
if(separator->IsDefined() ){
|
|
string sep = separator->GetValue();
|
|
sep = stringutils::replaceAll(sep,"\\t","\t");
|
|
if(sep.length()>0){
|
|
this->separator=sep;
|
|
}
|
|
}
|
|
if(quotes->IsDefined()){
|
|
this->quotes = quotes->GetValue();
|
|
}
|
|
|
|
BasicTuple = 0;
|
|
tupleType = 0;
|
|
defined = file->IsDefined() && hSize->IsDefined() &&
|
|
comment->IsDefined();
|
|
if(!defined){
|
|
return;
|
|
}
|
|
this->multiLine = false;
|
|
if(multiline && multiline->IsDefined()){
|
|
this->multiLine = multiline->GetBoolval();
|
|
}
|
|
this->comment = comment->GetValue();
|
|
useComment = this->comment.size()>0;
|
|
|
|
// Create csvinputstream
|
|
csvinputstream = new CSVBinFileInputStream(
|
|
file, &this->separator, &this->comment);
|
|
|
|
csvinputstream -> setMultiline(multiline);
|
|
csvinputstream -> setQuotes( quotes -> GetBoolval( ));
|
|
|
|
csvinputstream -> open();
|
|
|
|
if(! csvinputstream -> isUnprocessedDataAvailable() ){
|
|
defined = false;
|
|
return;
|
|
}
|
|
|
|
// skip header
|
|
int skip = hSize->GetIntval();
|
|
|
|
for(int i=0;i<skip && csvinputstream -> isUnprocessedDataAvailable();
|
|
i++) {
|
|
|
|
csvinputstream -> skipLine();
|
|
// Callback (needed for tcplb protocol)
|
|
csvinputstream -> tupleComplete();
|
|
}
|
|
|
|
if(!csvinputstream -> isUnprocessedDataAvailable()){
|
|
defined=false;
|
|
return;
|
|
}
|
|
|
|
ListExpr numType = nl->Second(
|
|
SecondoSystem::GetCatalog()->NumericType((type)));
|
|
tupleType = new TupleType(numType);
|
|
BasicTuple = new Tuple(tupleType);
|
|
// build instances for each type
|
|
ListExpr attrList = nl->Second(nl->Second(type));
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr attrType = nl->Second(nl->First(attrList));
|
|
attrList = nl->Rest(attrList);
|
|
int algId;
|
|
int typeId;
|
|
string tname;
|
|
if(! ((SecondoSystem::GetCatalog())->LookUpTypeExpr(attrType,
|
|
tname, algId, typeId))){
|
|
defined = false;
|
|
return;
|
|
}
|
|
Word w = am->CreateObj(algId,typeId)(attrType);
|
|
instances.push_back(static_cast<Attribute*>(w.addr));
|
|
}
|
|
}
|
|
|
|
~CsvImportInfo(){
|
|
if(BasicTuple){
|
|
delete BasicTuple;
|
|
BasicTuple = 0;
|
|
}
|
|
if(tupleType){
|
|
tupleType->DeleteIfAllowed();
|
|
tupleType = 0;
|
|
}
|
|
for(unsigned int i=0; i<instances.size();i++){
|
|
delete instances[i];
|
|
}
|
|
instances.clear();
|
|
|
|
if(csvinputstream) {
|
|
delete csvinputstream;
|
|
csvinputstream = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
bool isComment(string line){
|
|
if(!useComment){
|
|
return false;
|
|
} else {
|
|
return line.find(comment)==0;
|
|
}
|
|
}
|
|
|
|
Tuple* getNext(){
|
|
if(! csvinputstream -> isUnprocessedDataAvailable()){
|
|
return 0;
|
|
}
|
|
|
|
Tuple* res = createTuple();
|
|
|
|
if( ! defined ) {
|
|
return 0;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
bool defined;
|
|
bool useComment;
|
|
string comment;
|
|
TupleType* tupleType;
|
|
Tuple* BasicTuple;
|
|
vector<Attribute*> instances;
|
|
string separator;
|
|
bool quotes;
|
|
// support of multiline mode
|
|
bool multiLine;
|
|
CSVInputStream* csvinputstream;
|
|
|
|
Tuple* createTuple(){
|
|
static char c = 0;
|
|
static string nullstr(&c, 1);
|
|
Tuple* result = BasicTuple->Clone();
|
|
bool eol;
|
|
unsigned i = 0;
|
|
bool err;
|
|
|
|
while(i < instances.size()) {
|
|
Attribute* attr = instances[i]->Clone();
|
|
err = false;
|
|
|
|
if(csvinputstream -> isUnprocessedDataAvailable()) {
|
|
attr->ReadFromString(csvinputstream -> getNextValue(eol));
|
|
|
|
// incomplete line
|
|
if(eol && i < instances.size()-1) {
|
|
i = 0;
|
|
err = true;
|
|
cerr << "Error: More columns requested than "
|
|
<< "provided by import stream" << endl;
|
|
}
|
|
} else {
|
|
cerr << "Error: New tuple requested but no input "
|
|
<< "line available" << endl;
|
|
attr->SetDefined(false);
|
|
defined = false;
|
|
}
|
|
|
|
if(err){
|
|
attr->DeleteIfAllowed();
|
|
} else {
|
|
result->PutAttribute(i,attr);
|
|
i++;
|
|
}
|
|
|
|
}
|
|
|
|
if(! eol) {
|
|
cerr << "Warning: Line with more columns detected "
|
|
<< "than requested by tuple" << endl;
|
|
}
|
|
|
|
// Callback
|
|
csvinputstream -> tupleComplete();
|
|
|
|
return result;
|
|
}
|
|
};
|
|
|
|
|
|
template<class SType>
|
|
int csvimportVM1(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
|
|
CsvImportInfo* info = static_cast<CsvImportInfo*>(local.addr);
|
|
switch(message){
|
|
case OPEN: {
|
|
ListExpr type = qp->GetType(qp->GetSon(s,0));
|
|
SType* source = static_cast<SType*>(args[1].addr);
|
|
CcInt* skip = static_cast<CcInt*>(args[2].addr);
|
|
CcString* comment = static_cast<CcString*>(args[3].addr);
|
|
CcString* separator = static_cast<CcString*>(args[4].addr);
|
|
CcBool* quotes = static_cast<CcBool*>(args[5].addr);
|
|
CcBool* multiline = static_cast<CcBool*>(args[5].addr);
|
|
if(info){
|
|
delete info;
|
|
}
|
|
local.setAddr(new CsvImportInfo(type,source,skip,comment,separator,
|
|
quotes,multiline));
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
if(!info){
|
|
return CANCEL;
|
|
} else {
|
|
result.addr = info->getNext();
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
}
|
|
case CLOSE: {
|
|
if(info){
|
|
delete info;
|
|
local.addr=0;
|
|
}
|
|
|
|
}
|
|
default: {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Value Mapping array and Selection
|
|
|
|
*/
|
|
ValueMapping csvimportVM[] = {
|
|
csvimportVM1<FText>,
|
|
csvimportVM1<BinaryFile>
|
|
};
|
|
|
|
int csvimportSelect(ListExpr args){
|
|
|
|
return FText::checkType(nl->Second(args))?0:1;
|
|
|
|
}
|
|
|
|
/*
|
|
1.4 Operator Instance
|
|
|
|
*/
|
|
Operator csvimport( "csvimport",
|
|
csvimportSpec,
|
|
2,
|
|
csvimportVM,
|
|
csvimportSelect,
|
|
csvimportTM);
|
|
|
|
|
|
|
|
/*
|
|
1.5 Operator csvimport2
|
|
|
|
This operator does the same thing as the csv import operator.
|
|
The difference is that this version of the operator does not require
|
|
the relation scheme. Instead, the names of the attributes are read form the
|
|
first line of the csv file and all elements are assumed to be of type string.
|
|
|
|
1.5.1 Type Mapping
|
|
|
|
The signature is:
|
|
|
|
ftext x int x string x string x bool ->
|
|
stream(tuple( (X string) (Y string) ...))
|
|
|
|
filename, headersize, comment, separator, uses quotes, multiline
|
|
|
|
*/
|
|
|
|
|
|
|
|
ListExpr csvimport2TM(ListExpr args){
|
|
string err = "ftext x int x string x string x bool x bool expected";
|
|
if(!nl->HasLength(args,6)){
|
|
return listutils::typeError(err + " (wrong number of args)");
|
|
}
|
|
// this operator uses the arg evaluation within type mappings
|
|
// thus, each argument is a two elem list (type value)
|
|
ListExpr tmp = args;
|
|
while(!nl->IsEmpty(tmp)){
|
|
if(!nl->HasLength(nl->First(tmp),2)){
|
|
return listutils::typeError("expected (type value)");
|
|
}
|
|
tmp = nl->Rest(tmp);
|
|
}
|
|
// now, check the types
|
|
if(!FText::checkType(nl->First(nl->First(args)))){
|
|
return listutils::typeError(err + " (1st is not a text)");
|
|
}
|
|
|
|
if(!CcInt::checkType(nl->First(nl->Second(args)))){
|
|
return listutils::typeError(err + " (2nd is not an int)");
|
|
}
|
|
if(!CcString::checkType(nl->First(nl->Third(args)))){
|
|
return listutils::typeError(err + " (3th is not a string)");
|
|
}
|
|
if(!CcString::checkType(nl->First(nl->Fourth(args)))){
|
|
return listutils::typeError(err + " (4th is not a string)");
|
|
}
|
|
if(!CcBool::checkType(nl->First(nl->Fifth(args)))){
|
|
return listutils::typeError(err + " (5th is not a bool)");
|
|
}
|
|
if(!CcBool::checkType(nl->First(nl->Sixth(args)))){
|
|
return listutils::typeError(err + " (6th is not a bool)");
|
|
}
|
|
// ok, the types are fine, now, we have to evaluate the filename
|
|
// as well as the separator
|
|
string filenameExpr = nl->ToString(nl->Second(nl->First(args)));
|
|
Word res;
|
|
bool fnok = QueryProcessor::ExecuteQuery(filenameExpr,res);
|
|
if(!fnok){
|
|
return listutils::typeError("Could not get filename from expression");
|
|
}
|
|
FText* fn = (FText*) res.addr;
|
|
if(!fn->IsDefined()){
|
|
fn->DeleteIfAllowed();
|
|
return listutils::typeError("filename is undefined");
|
|
}
|
|
string filename = fn->GetValue();
|
|
fn->DeleteIfAllowed();
|
|
fn = 0;
|
|
res.setAddr(0);
|
|
// get the separator
|
|
string separatorExpr = nl->ToString(nl->Second(nl->Fourth(args)));
|
|
if(!QueryProcessor::ExecuteQuery(separatorExpr,res)){
|
|
return listutils::typeError("separator string could not be evaluated");
|
|
}
|
|
CcString* ccseparators = (CcString*) res.addr;
|
|
string separators = ","; // standard separator
|
|
if(ccseparators->IsDefined() && ccseparators->GetValue().size()>0){
|
|
separators = ccseparators->GetValue();
|
|
}
|
|
ccseparators->DeleteIfAllowed();
|
|
ccseparators = 0;
|
|
|
|
// get the first Line from the file
|
|
ifstream file(filename.c_str());
|
|
if(!file){
|
|
return listutils::typeError("could not open file " + filename);
|
|
}
|
|
string line;
|
|
getline(file,line);
|
|
if(!file.good()){
|
|
return listutils::typeError("could not read the first line of "
|
|
+ filename);
|
|
}
|
|
file.close();
|
|
|
|
stringutils::StringTokenizer st(line,separators);
|
|
int count = 0;
|
|
set<string> tokens;
|
|
|
|
ListExpr attrList= nl->TheEmptyList();
|
|
ListExpr last = attrList;
|
|
char c1 = 0;
|
|
string nullstr(&c1,1);;
|
|
|
|
while(st.hasNextToken()){
|
|
string token = st.nextToken();
|
|
stringutils::trim(token);
|
|
token = stringutils::replaceAll(token,nullstr,"");
|
|
if(token.size()<1){
|
|
stringstream ss;
|
|
ss << "Unknown" << count;
|
|
token = ss.str();
|
|
} else {
|
|
// convert token into a valid symbol
|
|
char f = token[0];
|
|
if(!stringutils::isLetter(f)){
|
|
token = "A_"+token;
|
|
} else {
|
|
token[0] = toupper(token[0]);
|
|
}
|
|
for(size_t i=1;i<token.size();i++){
|
|
char c = token[i];
|
|
if(!stringutils::isLetter(c) && !stringutils::isDigit(c)){
|
|
token[i] = '_';
|
|
}
|
|
}
|
|
}
|
|
string err;
|
|
if(!SecondoSystem::GetCatalog()->IsValidIdentifier(token,
|
|
true)){
|
|
stringstream ss;
|
|
ss << token << count;
|
|
token = ss.str();
|
|
}
|
|
|
|
// if there is a sequence of underlines within token,
|
|
// replace it by a single underline
|
|
while(token.find("__")!=string::npos){
|
|
token = stringutils::replaceAll(token,"__","_");
|
|
}
|
|
|
|
|
|
// avoid double names
|
|
int c2 = 0;
|
|
string t = token;
|
|
while( tokens.find(token)!=tokens.end()){
|
|
stringstream ss;
|
|
ss << t << "_" << c2;
|
|
c2++;
|
|
token = ss.str();
|
|
}
|
|
tokens.insert(token);
|
|
|
|
token = token.substr(0,MAX_STRINGSIZE);
|
|
ListExpr attr = nl->TwoElemList( nl->SymbolAtom(token),
|
|
listutils::basicSymbol<FText>());
|
|
if(count == 0){
|
|
attrList = nl->OneElemList( attr );
|
|
last = attrList;
|
|
} else {
|
|
last = nl->Append(last,attr);
|
|
}
|
|
count++;
|
|
}
|
|
|
|
return nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(
|
|
listutils::basicSymbol<Tuple>(),
|
|
attrList));
|
|
}
|
|
|
|
|
|
int csvimport2VM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
|
|
CsvImportInfo* li = (CsvImportInfo*) local.addr;
|
|
switch(message){
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
}
|
|
local.addr = new CsvImportInfo( qp->GetType(s),
|
|
(FText*) args[0].addr,
|
|
(CcInt*) args[1].addr,
|
|
(CcString*) args[2].addr,
|
|
(CcString*) args[3].addr,
|
|
(CcBool*) args[4].addr,
|
|
(CcBool*) args[5].addr);
|
|
return 0;
|
|
}
|
|
case REQUEST :{
|
|
result.addr=li?li->getNext():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
/*
|
|
1.5.3 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec csvimport2Spec(
|
|
"ftext x int x string x string x bool x bool-> stream(tuple(...))",
|
|
"csvimport2(filename, headersize, comment, separator, quotes, multilines)",
|
|
"csvimport2 imports the csv file given by filename into a relation"
|
|
" having text attributes exclusively. The file must contain "
|
|
" the attribute names within its first row separated by the same "
|
|
" character as the values. The second argument defines the size of"
|
|
" the header, this means the given number of rows is omitted from "
|
|
" the file. This number should be 1 at least. "
|
|
" Each line of the file starting with the string comment is ignored also."
|
|
" All characters within the separator argument are treat as attribute"
|
|
" separators. If this string is empty or undefined, a comma is used as "
|
|
"separator."
|
|
" Sometimes, the values are given in quotes (for example because "
|
|
" a string contains the separator char). If so,just set the "
|
|
" quotes argument to TRUE. "
|
|
" The parameter multilines can be used if attributes span more than 1 line."
|
|
"These attributes must be included in double quotes. If the multilines"
|
|
"argument is set to true, also the quotes argument is true.",
|
|
"query csvimport2('kinos.csv',1,\"\",\",\",TRUE,TRUE)");
|
|
|
|
|
|
/*
|
|
1.4 Operator Instance
|
|
|
|
*/
|
|
Operator csvimport2( "csvimport2",
|
|
csvimport2Spec.getStr(),
|
|
csvimport2VM,
|
|
Operator::SimpleSelect,
|
|
csvimport2TM);
|
|
|
|
|
|
/*
|
|
2 Operator nmeaImport
|
|
|
|
|
|
2.1 Type Mapping for nmeaimport
|
|
|
|
|
|
The signature is text x string -> stream(tuple(...))
|
|
|
|
The tuple type depends on the value of the second argument.
|
|
Because this operator evaulates the second argument in type mapping,
|
|
the types are extended to be pairs of (type, list), where list
|
|
is the representation of the value within the query.
|
|
|
|
Note: This type mapping is also used for operator nmeaimport[_]line.
|
|
If changes are made within this type mapping ensure to check for compatibility
|
|
with the nmeaimport[_]line.operator.
|
|
|
|
*/
|
|
static NMEAImporter* nmeaImporter=0;
|
|
|
|
ListExpr nmeaimportTM(ListExpr args){
|
|
|
|
string err = "text x string expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr first = nl->First(args);
|
|
ListExpr second = nl->Second(args);
|
|
|
|
if(!nl->HasLength(first,2) || !nl->HasLength(second,2)){
|
|
return listutils::typeError("invalid types for operator"
|
|
" evaluating arguments in type mapping");
|
|
}
|
|
ListExpr first1 = nl->First(first);
|
|
if(!listutils::isSymbol(first1,FText::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr second1 = nl->First(second);
|
|
if(!listutils::isSymbol(second1,CcString::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
// Evaulate the String value
|
|
ListExpr val = nl->Second(second);
|
|
|
|
Word res;
|
|
bool success = QueryProcessor::ExecuteQuery(nl->ToString(val),res);
|
|
if(!success){
|
|
return listutils::typeError("could not evaluate the value of string");
|
|
}
|
|
|
|
CcString* value = static_cast<CcString*>(res.addr);
|
|
|
|
if(!value->IsDefined()) {
|
|
value->DeleteIfAllowed();
|
|
return listutils::typeError("string argument evaluated to be undefined");
|
|
}
|
|
string v = value->GetValue();
|
|
value->DeleteIfAllowed();
|
|
|
|
if(!nmeaImporter){
|
|
nmeaImporter = new NMEAImporter();
|
|
}
|
|
if(!nmeaImporter->setType(v)){
|
|
return listutils::typeError(v + " is not a known nmea type id, known are "
|
|
+ nmeaImporter->getKnownTypes());
|
|
}
|
|
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nmeaImporter->getTupleType());
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
2.2 Value Mapping for nmeaimport
|
|
|
|
*/
|
|
int nmeaimportVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
switch(message){
|
|
case OPEN:{
|
|
if(local.addr){
|
|
delete (NMEAImporter*)local.addr;
|
|
local.addr=0;
|
|
}
|
|
FText* fn = static_cast<FText*>(args[0].addr);
|
|
CcString* t = static_cast<CcString*>(args[1].addr);
|
|
if(!fn->IsDefined() || !t->IsDefined()){
|
|
return 0;
|
|
}
|
|
NMEAImporter* imp = new NMEAImporter();
|
|
if(!imp->setType(t->GetValue())){
|
|
delete imp;
|
|
return 0;
|
|
}
|
|
string err;
|
|
if(!imp->scanFile(fn->GetValue(),err)){
|
|
delete imp;
|
|
return 0;
|
|
}
|
|
local.addr = imp;
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST:{
|
|
if(!local.addr){
|
|
return CANCEL;
|
|
}
|
|
NMEAImporter* imp = static_cast<NMEAImporter*>(local.addr);
|
|
Tuple* t = imp->nextTuple();
|
|
result.addr=t;
|
|
return t?YIELD:CANCEL;
|
|
}
|
|
|
|
case CLOSE:{
|
|
if(local.addr){
|
|
NMEAImporter* imp = static_cast<NMEAImporter*>(local.addr);
|
|
delete imp;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
default: assert(false);
|
|
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
2.3 Specification for nmeaimport
|
|
|
|
*/
|
|
|
|
const string nmeaimportSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> text x string -> streamtuple( TL )) </text--->"
|
|
"<text>nmeaimport( FileName, Type) </text--->"
|
|
"<text>Extracts data from the given text file 'FileName' as a tuple stream "
|
|
"(or an empty stream if the file contains no valid sentence). For each line"
|
|
"represents a correct NMEA 0183 sentence of of the specified 'Type', a "
|
|
"result tuple is created. The result tuple type TL depends on the specified "
|
|
"sentence type 'Type'. The NMEA 0183 sender prefix (first 2 "
|
|
"characters after the starting '$') is not considered! The known sentence "
|
|
"types are:"
|
|
// +nmeaImporter->getKnownTypes()+ // generic solution crashes at startup
|
|
"{GGA, GLL, GNS, GSA, GSV, RMC, ZDA}" // using hard-coded list instead!
|
|
".</text--->"
|
|
"<text> query nmeaimport('trip.nmea',\"GGA\") count</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.4 Operator instance for nmeaimport
|
|
|
|
*/
|
|
|
|
Operator nmeaimport( "nmeaimport",
|
|
nmeaimportSpec,
|
|
nmeaimportVM,
|
|
Operator::SimpleSelect,
|
|
nmeaimportTM);
|
|
|
|
|
|
|
|
/*
|
|
3 Operator nmeaimport[_]line
|
|
|
|
3.1 Type Mapping
|
|
|
|
Here, we just use the typoe mapping of the nmeaimport[_]line operator
|
|
|
|
|
|
3.2 Value Mapping
|
|
|
|
|
|
*/
|
|
|
|
int nmeaimport_lineVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
switch(message){
|
|
case OPEN:{
|
|
CcString* t = static_cast<CcString*>(args[1].addr);
|
|
if(!t->IsDefined()){
|
|
return 0;
|
|
}
|
|
string* type = new string(t->GetValue());
|
|
local.addr = type;
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST:{
|
|
if(!local.addr){
|
|
return CANCEL;
|
|
}
|
|
string* type = (string*) local.addr;
|
|
if(!nmeaImporter){
|
|
nmeaImporter = new NMEAImporter();
|
|
}
|
|
nmeaImporter->setType(*type);
|
|
delete type;
|
|
local.addr = 0;
|
|
FText* line = static_cast<FText*>(args[0].addr);
|
|
if(!line->IsDefined()){
|
|
return CANCEL;
|
|
}
|
|
result.addr = nmeaImporter->getTuple(line->GetValue());
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
|
|
case CLOSE:{
|
|
if(local.addr){
|
|
string* type = static_cast<string*>(local.addr);
|
|
delete type;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
default: assert(false);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
const string nmeaimport_lineSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>text x string -> stream(tuple( TL )) </text--->"
|
|
"<text>nmeaimport_line( Line, Type) </text--->"
|
|
"<text>Extracts data from the given text 'Line' as a single tuple (or an empty"
|
|
" stream if the line is not a valid sentence). The call is successful, if Line"
|
|
" represents a correct NMEA 0183 sentence of of the given Type. The result "
|
|
"tuple type TL depends on the specified sentence type ' Type'. The "
|
|
"NMEA 0183 sender prefix (first 2 characters after the starting '$') is not"
|
|
"considered! The known sentence types are: "
|
|
// +nmeaImporter->getKnownTypes()+ // generic solution crashes at startup
|
|
"{GGA, GLL, GNS, GSA, GSV, RMC, ZDA}" // using hard-coded list instead!
|
|
".</text--->"
|
|
"<text> query nmeaimport_line('$GPGGA,090150.383,"
|
|
"5131.2913,N,00726.9363,E,0,0,,102.5,M,47.5,M,,*45', \"GGA\") "
|
|
"tconsume</text--->"
|
|
") )";
|
|
|
|
|
|
/*
|
|
2.4 Operator instance for nmeaimport[_]line
|
|
|
|
*/
|
|
|
|
Operator nmeaimport_line( "nmeaimport_line",
|
|
nmeaimport_lineSpec,
|
|
nmeaimport_lineVM,
|
|
Operator::SimpleSelect,
|
|
nmeaimportTM);
|
|
|
|
|
|
/*
|
|
4 Operator ~get[_]lines~
|
|
|
|
This operator reads a file and returns all lines
|
|
found in this files into a stream.
|
|
|
|
|
|
4.1 Type Mapping
|
|
|
|
|
|
*/
|
|
ListExpr get_linesTM(ListExpr args){
|
|
string err = " string or text expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err + "(wrong number of args)");
|
|
}
|
|
|
|
ListExpr arg = nl->First(args);
|
|
if(!listutils::isSymbol(arg,CcString::BasicType()) &&
|
|
!listutils::isSymbol(arg,FText::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM() ),
|
|
nl->SymbolAtom(FText::BasicType()));
|
|
|
|
}
|
|
|
|
/*
|
|
4.2 ValueMapping for ~get[_]lines~
|
|
|
|
|
|
*/
|
|
template <class T>
|
|
int get_linesVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
switch(message){
|
|
case OPEN:{
|
|
if(local.addr){
|
|
delete (ifstream*)local.addr;
|
|
local.addr = 0;
|
|
}
|
|
T* arg = static_cast<T*>(args[0].addr);
|
|
if(!arg->IsDefined()){
|
|
return 0;
|
|
}
|
|
ifstream* in = new ifstream();
|
|
in->open(arg->GetValue().c_str());
|
|
local.addr = in;
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST:{
|
|
if(!local.addr){
|
|
return CANCEL;
|
|
}
|
|
ifstream* in = static_cast<ifstream*>(local.addr);
|
|
if(!in->good()){
|
|
return CANCEL;
|
|
}
|
|
string line;
|
|
getline(*in,line);
|
|
if(!in->good()){
|
|
return CANCEL;
|
|
}
|
|
result.addr = new FText(true, line);
|
|
return YIELD;
|
|
}
|
|
|
|
case CLOSE:{
|
|
if(local.addr){
|
|
ifstream* in = static_cast<ifstream*>(local.addr);
|
|
in->close();
|
|
delete in;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
ValueMapping get_linesMAPS[] = {get_linesVM<CcString>,
|
|
get_linesVM<FText>};
|
|
|
|
int get_linesSel(ListExpr args){
|
|
return listutils::isSymbol(nl->First(args), CcString::BasicType())?0:1;
|
|
}
|
|
|
|
|
|
|
|
const string get_linesSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>{text,string} -> stream(text)</text--->"
|
|
"<text>get_lines( FileName ) </text--->"
|
|
"<text>Returns the content of file 'FileName' line by line as text stream."
|
|
"</text--->"
|
|
"<text> query get_lines('ten.csv') count</text--->"
|
|
") )";
|
|
|
|
|
|
Operator get_lines( "get_lines",
|
|
get_linesSpec,
|
|
2,
|
|
get_linesMAPS,
|
|
get_linesSel,
|
|
get_linesTM);
|
|
|
|
|
|
/*
|
|
2 Operator shpExport
|
|
|
|
2.1 Type Mapping
|
|
|
|
*/
|
|
|
|
ListExpr shpexportTM(ListExpr args){
|
|
int len = nl->ListLength(args);
|
|
if((len!=2) && (len != 3) && (len !=4)){
|
|
ErrorReporter::ReportError("wrong number of arguments");
|
|
return nl->TypeError();
|
|
}
|
|
string err = " stream(SHPEXPORTABLE) x text [ x text ]\n "
|
|
" or stream(tuple(...)) x text x attrname [x text] expected";
|
|
|
|
// the second argument must be a text
|
|
if(!FText::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr stream = nl->First(args);
|
|
if(Stream<Tuple>::checkType(stream)){
|
|
if(len<3){
|
|
return listutils::typeError(err);
|
|
}
|
|
// case stream(tuple) x text x attrname [ x text]
|
|
ListExpr an = nl->Third(args);
|
|
if(!listutils::isSymbol(an)){
|
|
return listutils::typeError(err + ": invalid attribute name");
|
|
}
|
|
ListExpr type;
|
|
string name = nl->SymbolValue(an);
|
|
ListExpr attrList = nl->Second(nl->Second(stream));
|
|
int pos = listutils::findAttribute(attrList, name, type);
|
|
if(pos<1){
|
|
return listutils::typeError(err + ": attribute " + name +
|
|
" not member of tuple");
|
|
}
|
|
pos--;
|
|
ListExpr errorInfo = listutils::emptyErrorInfo();
|
|
// type of attribute must be in KIND SHPEXORTABLE
|
|
if(!am->CheckKind(Kind::SHPEXPORTABLE(),type, errorInfo)){
|
|
return listutils::typeError(err + ":Attribute "+ name +
|
|
"not in kind SHPEXPORTABLE");
|
|
}
|
|
if(len==4){
|
|
ListExpr shx = nl->Fourth(args);
|
|
if(!FText::checkType(shx)){
|
|
return listutils::typeError(err);
|
|
}
|
|
// all the things are ok
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->OneElemList( nl->IntAtom(pos)),
|
|
stream);
|
|
} else {
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->TwoElemList(nl->TextAtom(""),
|
|
nl->IntAtom(pos)),
|
|
stream);
|
|
}
|
|
}
|
|
// check for stream<SHPEXPORTABLE>
|
|
if(!nl->HasLength(stream,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
if(!listutils::isSymbol(nl->First(stream), Stream<Attribute>::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
if(len==3){ // having file name for idx file
|
|
ListExpr idx = nl->Third(args);
|
|
if(!FText::checkType(idx)){
|
|
return listutils::typeError(err);
|
|
}
|
|
return stream;
|
|
} else {
|
|
// no name for idx file is given
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->OneElemList(nl->TextAtom("")),
|
|
stream);
|
|
}
|
|
}
|
|
|
|
/*
|
|
2.2 Value Mapping
|
|
|
|
*/
|
|
|
|
class shpLInfo{
|
|
public:
|
|
shpLInfo(FText* name, FText* idxname){
|
|
if(!name->IsDefined()){
|
|
defined = false;
|
|
} else {
|
|
defined = true;
|
|
first = true;
|
|
baseName = (name->GetValue());
|
|
recno = 1;
|
|
boxdef = false;
|
|
xMin = 0;
|
|
xMax = 0;
|
|
yMin = 0;
|
|
yMax = 0;
|
|
zMin = 0;
|
|
zMax = 0;
|
|
mMin = 0;
|
|
mMax = 0;
|
|
if(!idxname->IsDefined() ||
|
|
(idxname->GetValue()).length()==0){
|
|
produceIdx = false;
|
|
} else {
|
|
produceIdx = true;
|
|
idxName = idxname->GetValue();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool writeHeader(uint32_t type){
|
|
file.open((baseName ).c_str(),
|
|
ios::out | ios::trunc | ios_base::binary);
|
|
if(!file.good()){
|
|
return false;
|
|
}
|
|
uint32_t code = 9994;
|
|
WinUnix::writeBigEndian(file,code);
|
|
uint32_t unused = 0;
|
|
for(int i=0;i<5;i++){
|
|
WinUnix::writeBigEndian(file,unused);
|
|
}
|
|
// dummy for file length
|
|
uint32_t length = 100;
|
|
WinUnix::writeBigEndian(file,length);
|
|
uint32_t version = 1000;
|
|
WinUnix::writeLittleEndian(file,version);
|
|
WinUnix::writeLittleEndian(file,type);
|
|
double boxpos = 0.0;
|
|
for(int i=0;i<8;i++){
|
|
WinUnix::writeLittle64(file,boxpos);
|
|
}
|
|
|
|
if(produceIdx){
|
|
idxfile.open((idxName ).c_str(),
|
|
ios::out | ios::trunc | ios_base::binary);
|
|
if(!idxfile.good()){
|
|
produceIdx = false;
|
|
return file.good();
|
|
}
|
|
uint32_t code = 9994;
|
|
WinUnix::writeBigEndian(idxfile,code);
|
|
uint32_t unused = 0;
|
|
for(int i=0;i<5;i++){
|
|
WinUnix::writeBigEndian(idxfile,unused);
|
|
}
|
|
// dummy for file length
|
|
uint32_t length = 100;
|
|
WinUnix::writeBigEndian(idxfile,length);
|
|
uint32_t version = 1000;
|
|
WinUnix::writeLittleEndian(idxfile,version);
|
|
WinUnix::writeLittleEndian(idxfile,type);
|
|
double boxpos = 0.0;
|
|
for(int i=0;i<8;i++){
|
|
WinUnix::writeLittle64(idxfile,boxpos);
|
|
}
|
|
}
|
|
return file.good();
|
|
}
|
|
|
|
bool write(Attribute* value) {
|
|
if(!file.good() || ! defined ){
|
|
return false;
|
|
}
|
|
if(first){
|
|
if(!writeHeader(value->getshpType())){
|
|
return false;
|
|
}
|
|
first = false;
|
|
}
|
|
uint32_t offset = file.tellp() /2; // measured as 16 bit words
|
|
value->writeShape(file,recno++);
|
|
if(value->hasBox()){
|
|
if(!boxdef){
|
|
xMin = value->getMinX();
|
|
xMax = value->getMaxX();
|
|
yMin = value->getMinY();
|
|
yMax = value->getMaxY();
|
|
zMin = value->getMinZ();
|
|
zMax = value->getMaxZ();
|
|
mMin = value->getMinM();
|
|
mMax = value->getMaxM();
|
|
boxdef=true;
|
|
} else {
|
|
xMin = min(xMin,value->getMinX());
|
|
xMax = max(xMax,value->getMaxX());
|
|
yMin = min(yMin,value->getMinY());
|
|
yMax = max(yMax,value->getMaxY());
|
|
zMin = min(zMin,value->getMinZ());
|
|
zMax = max(zMax,value->getMaxZ());
|
|
mMin = min(mMin,value->getMinM());
|
|
mMax = max(mMax,value->getMaxM());
|
|
}
|
|
}
|
|
uint32_t contentlength = file.tellp()/2 - 4 - offset;
|
|
if(produceIdx){
|
|
WinUnix::writeBigEndian(idxfile, offset);
|
|
WinUnix::writeBigEndian(idxfile, contentlength);
|
|
}
|
|
|
|
return file.good();
|
|
}
|
|
|
|
|
|
|
|
void close(){
|
|
// write correct file-length into file
|
|
// write correct bounding box into file
|
|
uint32_t len = file.tellp() / 2; // measured in 16 bit words
|
|
file.seekp(24,ios_base::beg);
|
|
WinUnix::writeBigEndian(file,len);
|
|
file.seekp(36,ios_base::beg);
|
|
WinUnix::writeLittle64(file,xMin);
|
|
WinUnix::writeLittle64(file,yMin);
|
|
WinUnix::writeLittle64(file,xMax);
|
|
WinUnix::writeLittle64(file,yMax);
|
|
WinUnix::writeLittle64(file,zMin);
|
|
WinUnix::writeLittle64(file,zMax);
|
|
WinUnix::writeLittle64(file,mMin);
|
|
WinUnix::writeLittle64(file,mMax);
|
|
file.close();
|
|
|
|
if(produceIdx){
|
|
uint32_t len = idxfile.tellp() / 2;
|
|
idxfile.seekp(24,ios_base::beg);
|
|
WinUnix::writeBigEndian(idxfile,len);
|
|
idxfile.seekp(36,ios_base::beg);
|
|
WinUnix::writeLittle64(idxfile,xMin);
|
|
WinUnix::writeLittle64(idxfile,yMin);
|
|
WinUnix::writeLittle64(idxfile,xMax);
|
|
WinUnix::writeLittle64(idxfile,yMax);
|
|
WinUnix::writeLittle64(idxfile,zMin);
|
|
WinUnix::writeLittle64(idxfile,zMax);
|
|
WinUnix::writeLittle64(idxfile,mMin);
|
|
WinUnix::writeLittle64(idxfile,mMax);
|
|
idxfile.close();
|
|
}
|
|
|
|
}
|
|
|
|
int getIndex(){ return index; }
|
|
void setIndex(int index) {this->index = index;}
|
|
|
|
private:
|
|
string baseName;
|
|
bool first;
|
|
bool defined;
|
|
ofstream file;
|
|
int recno;
|
|
double xMin;
|
|
double xMax;
|
|
double yMin;
|
|
double yMax;
|
|
double zMin;
|
|
double zMax;
|
|
double mMin;
|
|
double mMax;
|
|
bool boxdef;
|
|
int index;
|
|
bool produceIdx;
|
|
string idxName;
|
|
ofstream idxfile;
|
|
|
|
};
|
|
|
|
int shpexportVM1(Word* args, Word& result,
|
|
int message, Word& local, Supplier s)
|
|
{
|
|
switch( message )
|
|
{
|
|
case OPEN:{
|
|
qp->Open(args[0].addr);
|
|
FText* fname = static_cast<FText*>(args[1].addr);
|
|
FText* idxname = static_cast<FText*>(args[2].addr);
|
|
local.setAddr(new shpLInfo(fname,idxname));
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
shpLInfo* linfo= static_cast<shpLInfo*>(local.addr);
|
|
if(!linfo){
|
|
return CANCEL;
|
|
}
|
|
Word elem;
|
|
qp->Request(args[0].addr, elem);
|
|
if(qp->Received(args[0].addr)){
|
|
bool ok;
|
|
ok = linfo->write( static_cast<Attribute*>(elem.addr));
|
|
ok = true;
|
|
if(ok){
|
|
result = elem;
|
|
return YIELD;
|
|
} else {
|
|
Attribute* del = static_cast<Attribute*>(elem.addr);
|
|
del->DeleteIfAllowed();
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
} else {
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
}
|
|
case CLOSE:{
|
|
qp->Close(args[0].addr);
|
|
if(local.addr){
|
|
shpLInfo* linfo = static_cast<shpLInfo*>(local.addr);
|
|
linfo->close();
|
|
delete linfo;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
int shpexportVM2(Word* args, Word& result,
|
|
int message, Word& local, Supplier s)
|
|
{
|
|
switch( message )
|
|
{
|
|
case OPEN:{
|
|
qp->Open(args[0].addr);
|
|
FText* fname = static_cast<FText*>(args[1].addr);
|
|
FText* idxname = static_cast<FText*>(args[3].addr);
|
|
|
|
cout << "fname = " << fname->GetValue() << endl;
|
|
cout << "idxname = " << idxname->GetValue() << endl;
|
|
|
|
shpLInfo* linfo = new shpLInfo(fname, idxname);
|
|
int attrPos = (static_cast<CcInt*>(args[4].addr))->GetIntval();
|
|
|
|
cout << " attrIndex = " << attrPos << endl;
|
|
|
|
linfo->setIndex( attrPos);
|
|
local.setAddr(linfo);
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
shpLInfo* linfo= static_cast<shpLInfo*>(local.addr);
|
|
if(!linfo){
|
|
return CANCEL;
|
|
}
|
|
Word elem;
|
|
qp->Request(args[0].addr, elem);
|
|
if(qp->Received(args[0].addr)){
|
|
Tuple* tuple = static_cast<Tuple*>(elem.addr);
|
|
Attribute* attr;
|
|
int attrPos = linfo->getIndex();
|
|
attr = tuple->GetAttribute(attrPos);
|
|
linfo->write(attr);
|
|
result = elem;
|
|
return YIELD;
|
|
} else {
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
}
|
|
case CLOSE:{
|
|
qp->Close(args[0].addr);
|
|
if(local.addr){
|
|
shpLInfo* linfo = static_cast<shpLInfo*>(local.addr);
|
|
linfo->close();
|
|
delete linfo;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
2.3 Value Mapping Array
|
|
|
|
*/
|
|
|
|
ValueMapping shpexportmap[] =
|
|
{ shpexportVM1, shpexportVM2};
|
|
|
|
/*
|
|
2.4 Selection Function
|
|
|
|
*/
|
|
|
|
int shpexportSelect( ListExpr args )
|
|
{ if(!listutils::isTupleStream(nl->First(args))){
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
2.5 Specification
|
|
|
|
*/
|
|
const string shpexportSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(T) x text [x text] -> stream(T), X in SHPEXPORTABLE\n"
|
|
"stream(tuple(T))) x text x attrname [x text] -> stream(tuple(T))"
|
|
", (attrname W) in T, W in SHPEXPORTABLE</text--->"
|
|
"<text> stream shpexport [ shpfile [, idxfile] ]\n"
|
|
"tuplestream shpexport[ shpfile, attrname [, idxfile] ]</text--->"
|
|
"<text> Exports stream content to the specified shp file. If the optional"
|
|
"argument is provided, also the according shape index file is created."
|
|
"</text--->"
|
|
"<text>query Kinos feed projecttransformstream[geoData] "
|
|
"shpexport['kinos.shp'] count</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.6 Operator Instance
|
|
|
|
*/
|
|
|
|
Operator shpexport( "shpexport",
|
|
shpexportSpec,
|
|
2,
|
|
shpexportmap,
|
|
shpexportSelect,
|
|
shpexportTM);
|
|
|
|
|
|
/*
|
|
3 DB3-export
|
|
|
|
3.1 Type Mapping
|
|
|
|
stream(tuple ( ... )) x string -> stream(tuple(...))
|
|
|
|
*/
|
|
|
|
|
|
|
|
ListExpr db3exportTM(ListExpr args){
|
|
string err = "stream(tuple ( ... )) x text expected";
|
|
if(nl->ListLength(args)!=2){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
if(!IsStreamDescription(nl->First(args)) ||
|
|
!nl->IsEqual(nl->Second(args),FText::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
return nl->First(args);
|
|
}
|
|
|
|
/*
|
|
3.2 Value Mapping
|
|
|
|
*/
|
|
|
|
class Db3LInfo{
|
|
public:
|
|
Db3LInfo(FText* fname, ListExpr type){
|
|
if(!fname->IsDefined()){ // no correct filename
|
|
defined=false;
|
|
} else {
|
|
if(!f.good()){
|
|
defined = false;
|
|
} else {
|
|
this->fname = (fname->GetValue());
|
|
first = true;
|
|
firstMemo=true;
|
|
defined = true;
|
|
memNumber = 0;
|
|
recNumber = 0;
|
|
ListExpr rest = nl->Second(nl->Second(type));
|
|
while(!nl->IsEmpty(rest)){
|
|
string name = nl->SymbolValue(nl->First(nl->First(rest)));
|
|
rest = nl->Rest(rest);
|
|
name = name.substr(0,10);// allow exactly 11 chars
|
|
names.push_back(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeHeader(Tuple* tuple){
|
|
bool hasMemo = false;
|
|
uint16_t recSize = 1; // deleted marker
|
|
int exportable = 0;
|
|
for(int i=0;i<tuple->GetNoAttributes();i++){
|
|
Attribute* attr = tuple->GetAttribute(i);
|
|
if(attr->hasDB3Representation()){
|
|
exportable++;
|
|
bool ismemo = false;
|
|
if(attr->getDB3Type() == 'M'){
|
|
hasMemo = true;
|
|
ismemo = true;
|
|
}
|
|
unsigned char len = attr->getDB3Length();
|
|
if(ismemo){
|
|
len = 10;
|
|
}
|
|
recSize += len;
|
|
exps.push_back(true);
|
|
lengths.push_back(len);
|
|
dbtypes.push_back(attr->getDB3Type());
|
|
decCnts.push_back(attr->getDB3DecimalCount());
|
|
isMemo.push_back(ismemo);
|
|
} else {
|
|
exps.push_back(false);
|
|
lengths.push_back(0);
|
|
dbtypes.push_back('L');
|
|
decCnts.push_back(0);
|
|
isMemo.push_back(false);
|
|
}
|
|
}
|
|
if(exportable == 0){ // no exportable attributes found
|
|
defined = false;
|
|
return;
|
|
}
|
|
// open the file
|
|
f.open((this->fname).c_str(),
|
|
ios::out | ios::trunc | ios::binary);
|
|
|
|
if(!f.good()){
|
|
defined = false;
|
|
return;
|
|
}
|
|
|
|
unsigned char code = hasMemo?0x83:0x03;
|
|
WinUnix::writeLittleEndian(f,code);
|
|
// wrint an dummy date
|
|
unsigned char yy = 99;
|
|
unsigned char mm = 01;
|
|
unsigned char dd = 01;
|
|
f << yy << mm << dd;
|
|
uint32_t num = 1;
|
|
WinUnix::writeLittleEndian(f,num); // dummy number of records
|
|
// header size
|
|
|
|
uint16_t headersize = 33 + exportable*32;
|
|
WinUnix::writeLittleEndian(f,headersize);
|
|
|
|
// record size
|
|
WinUnix::writeLittleEndian(f,recSize);
|
|
unsigned char reserved=0;
|
|
for(int i=0;i<20;i++){
|
|
WinUnix::writeLittleEndian(f,reserved);
|
|
}
|
|
unsigned char zero = 0;
|
|
for(unsigned int i=0; i< names.size(); i++){
|
|
if(exps[i]){
|
|
// write Field descriptor
|
|
string name = names[i];
|
|
int len = name.length();
|
|
f << name;
|
|
// fill with zeros
|
|
for(int j=len;j<11;j++){
|
|
f << zero;
|
|
}
|
|
// type
|
|
WinUnix::writeLittleEndian(f,dbtypes[i]);
|
|
// address in memory
|
|
uint32_t adr = 0;
|
|
WinUnix::writeLittleEndian(f,adr);
|
|
// len
|
|
WinUnix::writeLittleEndian(f,lengths[i]);
|
|
// decimal count
|
|
WinUnix::writeLittleEndian(f,decCnts[i]);
|
|
for(int j=0;j< 14; j++){
|
|
f << reserved;
|
|
}
|
|
}
|
|
|
|
}
|
|
unsigned char term = 0x0D;
|
|
WinUnix::writeLittleEndian(f,term);
|
|
if(!f.good()){
|
|
f.close();
|
|
defined = false;
|
|
}
|
|
}
|
|
|
|
|
|
void writeMemo(string memo){
|
|
string no =" "; // 10 spaces
|
|
if(firstMemo){
|
|
string memoname;
|
|
bool endsWith = true;
|
|
if(fname.size() < 4){
|
|
endsWith = false;
|
|
} else {
|
|
size_t s = fname.size();
|
|
endsWith = (fname[s-4] == '.') &&
|
|
((fname[s-3]=='d') || (fname[s-3]=='D') ) &&
|
|
((fname[s-2]=='b') || (fname[s-2]=='B') ) &&
|
|
((fname[s-1]=='f') || (fname[s-1]=='F') ) ;
|
|
}
|
|
if(endsWith){
|
|
memoname = fname;
|
|
size_t p = fname.size()-1;
|
|
if(memoname[p] == 'f'){
|
|
memoname[p] = 't';
|
|
} else {
|
|
memoname[p] = 'T';
|
|
}
|
|
} else {
|
|
memoname = fname +".dbt";
|
|
}
|
|
fmemo.open(memoname.c_str(),
|
|
ios::out | ios::trunc | ios::binary);
|
|
memNumber = 1;
|
|
firstMemo = false;
|
|
// write the header
|
|
unsigned char zero = 0;
|
|
unsigned char version = 3;
|
|
for(int i=0;i<512;i++){
|
|
if(i==16){
|
|
fmemo << version;
|
|
} else {
|
|
fmemo << zero;
|
|
}
|
|
}
|
|
}
|
|
// empty memo or in
|
|
if(!fmemo.good() || memo.length()==0){
|
|
f << no;
|
|
return;
|
|
}
|
|
// write block number to f
|
|
stringstream ss;
|
|
ss << memNumber;
|
|
no = extendString(ss.str(),10,0);
|
|
f << no;
|
|
// write data to fmemo
|
|
fmemo << memo;
|
|
unsigned char term = 0x1A;
|
|
fmemo << term << term;
|
|
unsigned int len = memo.length() + 2 ;
|
|
unsigned int blocks = len / 512;
|
|
unsigned int os = len % 512;
|
|
if(os!=0){
|
|
blocks++;
|
|
}
|
|
unsigned char zero = 0;
|
|
// fill the block with zero
|
|
while(os!=0){
|
|
fmemo << zero;
|
|
os = (os + 1 ) % 512;
|
|
}
|
|
memNumber += blocks;
|
|
|
|
}
|
|
|
|
void write(Tuple* tuple){
|
|
if(!defined){ // invalid filename input
|
|
return;
|
|
}
|
|
if(first){
|
|
writeHeader(tuple);
|
|
first = false;
|
|
}
|
|
if(!defined){ // no exportable attributes
|
|
return;
|
|
}
|
|
recNumber++;
|
|
|
|
f << ' '; // mark record as non-deleted
|
|
for(unsigned int i=0;i<names.size();i++){
|
|
if(exps[i]){
|
|
unsigned char len = lengths[i];
|
|
string s = (tuple->GetAttribute(i))->getDB3String();
|
|
bool ismemo = isMemo[i];
|
|
if(!ismemo){
|
|
char e = dbtypes[i]=='C'?0:' ';
|
|
s = extendString(s,len,e);
|
|
f << s;
|
|
} else {
|
|
writeMemo(s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
string extendString(string s, int len,const char c){
|
|
s = s.substr(0,len); // shrink to maximum length
|
|
int e = len-s.length();
|
|
stringstream ss;
|
|
ss << s;
|
|
for(int i=0;i<e; i++){
|
|
ss << c;
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
void close() {
|
|
if(defined){
|
|
unsigned char eof = 0x1A;
|
|
f << eof;
|
|
f.seekp(4,ios::beg);
|
|
WinUnix::writeLittleEndian(f,recNumber);
|
|
f.seekp(0,ios::end);
|
|
f.close();
|
|
|
|
// close memo if used
|
|
if(!firstMemo){
|
|
fmemo.seekp(0,ios::beg);
|
|
WinUnix::writeLittleEndian(fmemo,memNumber);
|
|
fmemo.seekp(0,ios::end);
|
|
fmemo.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
fstream f;
|
|
fstream fmemo;
|
|
bool defined;
|
|
bool first;
|
|
bool firstMemo;
|
|
uint32_t memNumber;
|
|
vector<string> names; // names for the attributes
|
|
vector<unsigned char> lengths; // lengths for the attributes
|
|
vector<bool> exps; // flags whether the attribute is exportable
|
|
vector<unsigned char> dbtypes;
|
|
vector<unsigned char> decCnts;
|
|
vector<bool> isMemo;
|
|
string fname;
|
|
|
|
uint32_t recNumber; // number of contained records
|
|
};
|
|
|
|
|
|
int db3exportVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
switch( message )
|
|
{
|
|
case OPEN:{
|
|
qp->Open(args[0].addr);
|
|
FText* fname = static_cast<FText*>(args[1].addr);
|
|
Db3LInfo* linfo = new Db3LInfo(fname,qp->GetType(s));
|
|
local.setAddr(linfo);
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
Db3LInfo* linfo= static_cast<Db3LInfo*>(local.addr);
|
|
if(!linfo){
|
|
return CANCEL;
|
|
}
|
|
Word elem;
|
|
qp->Request(args[0].addr, elem);
|
|
if(qp->Received(args[0].addr)){
|
|
Tuple* tuple = static_cast<Tuple*>(elem.addr);
|
|
linfo->write(tuple);
|
|
result = elem;
|
|
return YIELD;
|
|
} else {
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
}
|
|
case CLOSE:{
|
|
qp->Close(args[0].addr);
|
|
if(local.addr){
|
|
Db3LInfo* linfo = static_cast<Db3LInfo*>(local.addr);
|
|
linfo->close();
|
|
delete linfo;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
3.3 Specification
|
|
|
|
*/
|
|
|
|
const string db3exportSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> stream(tuple(...))) x text -> stream(tuple...)</text--->"
|
|
"<text> stream db3export [ file ]</text--->"
|
|
"<text> Exports stream content to a db3 file </text--->"
|
|
"<text> not tested !!!</text--->"
|
|
") )";
|
|
|
|
/*
|
|
3.4 Operator Instance
|
|
|
|
*/
|
|
Operator db3export( "db3export",
|
|
db3exportSpec,
|
|
db3exportVM,
|
|
Operator::SimpleSelect,
|
|
db3exportTM);
|
|
|
|
|
|
|
|
/*
|
|
4 Operator shptype
|
|
|
|
|
|
4.1 Type Mapping
|
|
|
|
text -> text
|
|
|
|
*/
|
|
|
|
ListExpr shptypeTM(ListExpr args){
|
|
if(nl->ListLength(args)==1 && nl->IsEqual(nl->First(args),
|
|
FText::BasicType())){
|
|
return nl->SymbolAtom(FText::BasicType());
|
|
}
|
|
ErrorReporter::ReportError("text expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
/*
|
|
4.2 Value mapping
|
|
|
|
*/
|
|
|
|
/*
|
|
This function retrieves the type of a
|
|
|
|
*/
|
|
string getShpType(const string fname, bool& correct, string& errorMessage){
|
|
correct = false;
|
|
if(fname.length()==0){
|
|
errorMessage = "invalid filename";
|
|
return "";
|
|
}
|
|
|
|
ifstream f(fname.c_str(),std::ios::binary);
|
|
if(!f.good()){
|
|
errorMessage = "problem in reading file";
|
|
return "";
|
|
}
|
|
|
|
f.seekg(0,ios::end);
|
|
streampos flen = f.tellg();
|
|
if(flen < 100){
|
|
errorMessage = "not a valid shape file";
|
|
f.close();
|
|
return "";
|
|
}
|
|
|
|
|
|
f.seekg(0,ios::beg);
|
|
uint32_t code = 0;
|
|
f.read(reinterpret_cast<char*>(&code),4);
|
|
if(WinUnix::isLittleEndian()){
|
|
code = WinUnix::convertEndian(code);
|
|
}
|
|
if(code!=9994){
|
|
errorMessage = "invalid file code detected";
|
|
f.close();
|
|
return "";
|
|
}
|
|
|
|
uint32_t version;
|
|
f.seekg(28,ios::beg);
|
|
f.read(reinterpret_cast<char*>(&version),4);
|
|
if(!WinUnix::isLittleEndian()){
|
|
version = WinUnix::convertEndian(version);
|
|
}
|
|
if(version != 1000){
|
|
errorMessage = "invalid version detected";
|
|
f.close();
|
|
return "";
|
|
}
|
|
uint32_t type;
|
|
f.read(reinterpret_cast<char*>(&type),4);
|
|
if(!WinUnix::isLittleEndian()){
|
|
type = WinUnix::convertEndian(type);
|
|
}
|
|
f.close();
|
|
|
|
switch(type){
|
|
case 0 : { errorMessage = "null shape, no corresponding secondo type";
|
|
return "";
|
|
}
|
|
case 1 : { correct = true;
|
|
return Point::BasicType();
|
|
}
|
|
case 3 : { correct = true;
|
|
return Line::BasicType();
|
|
}
|
|
case 5 : { correct = true;
|
|
return Region::BasicType();
|
|
}
|
|
case 8 : { correct = true;
|
|
return Points::BasicType();
|
|
}
|
|
case 11 : { errorMessage = "PointZ, no corresponding secondo type";
|
|
return "";
|
|
}
|
|
case 13 : { errorMessage = ("PolyLineZ, no corresponding secondo type");
|
|
return "";
|
|
}
|
|
case 15 : { errorMessage = ("PolygonZ, no corresponding secondo type");
|
|
return "";
|
|
}
|
|
case 18 : { errorMessage=("MultiPointZ, no corresponding secondo type");
|
|
return "";
|
|
}
|
|
case 21 : { errorMessage=("PointM, no corresponding secondo type");
|
|
return "";
|
|
}
|
|
case 23 : { errorMessage =("PolyLineM, no corresponding secondo type");
|
|
return "";
|
|
}
|
|
case 25 : {errorMessage=("PolygonM, no corresponding secondo type");
|
|
return "";
|
|
}
|
|
case 28 : { errorMessage=("MultiPointM, no corresponding secondo type");
|
|
return "";
|
|
}
|
|
case 31 : { errorMessage = ("MultiPatch, no corresponding secondo type");
|
|
return "";
|
|
}
|
|
default : errorMessage = " not a valid shape type";
|
|
return "";
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int shptypeVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
FText* res = static_cast<FText*>(result.addr);
|
|
FText* FN = static_cast<FText*>(args[0].addr);
|
|
if(!FN->IsDefined()){
|
|
res->Set(false,"");
|
|
return 0;
|
|
}
|
|
bool correct;
|
|
string shpType;
|
|
string errMsg;
|
|
shpType = getShpType(FN->GetValue(), correct, errMsg);
|
|
if(!correct){
|
|
res->Set(true, errMsg);
|
|
return 0;
|
|
}
|
|
string value = "()";
|
|
if(shpType==Point::BasicType()){
|
|
value = "(0 0)";
|
|
}
|
|
res->Set(true, "[const "+shpType+" value "+value+"]");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
4.3 Specification
|
|
|
|
*/
|
|
|
|
const string shptypeSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> text -> text</text--->"
|
|
"<text> shptype(filename) </text--->"
|
|
"<text> returns an object description of the secondo object"
|
|
" stored within the shape file specified by the name</text--->"
|
|
"<text> not tested !!!</text--->"
|
|
") )";
|
|
|
|
|
|
|
|
/*
|
|
4.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator shptype( "shptype",
|
|
shptypeSpec,
|
|
shptypeVM,
|
|
Operator::SimpleSelect,
|
|
shptypeTM);
|
|
|
|
|
|
|
|
/*
|
|
5 Operator shpimport
|
|
|
|
5.1 Type Mapping
|
|
|
|
s x text -> stream(s), s in {point, points, line, region}
|
|
|
|
*/
|
|
|
|
ListExpr shpimportTM(ListExpr args){
|
|
string err =
|
|
" s x text -> stream(s), s in {point, points, line, region} expected";
|
|
if(nl->ListLength(args)!=2){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
if(!nl->IsEqual(nl->Second(args),FText::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr arg1 = nl->First(args);
|
|
if(nl->AtomType(arg1)!=SymbolType){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
string t1 = nl->SymbolValue(arg1);
|
|
|
|
if(t1!=Point::BasicType() &&
|
|
t1!=Points::BasicType() &&
|
|
t1!=Line::BasicType() &&
|
|
t1!=Region::BasicType()){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()), arg1);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
5.2 Value mapping
|
|
|
|
*/
|
|
|
|
class shpimportInfo{
|
|
|
|
public:
|
|
|
|
shpimportInfo( const ListExpr allowedType1, const FText* fname){
|
|
buffer = 0;
|
|
int allowedType = -1;
|
|
if(listutils::isSymbol(allowedType1,Point::BasicType())){
|
|
allowedType=1;
|
|
} else if(listutils::isSymbol(allowedType1,Line::BasicType())){
|
|
allowedType=3;
|
|
} else if(listutils::isSymbol(allowedType1,Region::BasicType())){
|
|
allowedType=5;
|
|
} else if(listutils::isSymbol(allowedType1,Points::BasicType())){
|
|
allowedType=8;
|
|
}
|
|
|
|
|
|
if(!fname->IsDefined()){
|
|
defined = false;
|
|
} else {
|
|
defined = true;
|
|
string name = fname->GetValue();
|
|
this->filename = name;
|
|
file.open(name.c_str(),ios::binary);
|
|
buffer = new char[FILE_BUFFER_SIZE];
|
|
file.rdbuf()->pubsetbuf(buffer, FILE_BUFFER_SIZE);
|
|
|
|
if(!file.good()){
|
|
defined = false;
|
|
file.close();
|
|
} else {
|
|
defined = readHeader(allowedType);
|
|
if(!defined){
|
|
file.close();
|
|
} else {
|
|
file.seekg(100,ios::beg); // to the first data set
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
~shpimportInfo(){
|
|
if(buffer){
|
|
delete[] buffer;
|
|
}
|
|
}
|
|
|
|
|
|
Attribute* getNext(){
|
|
if(!defined){
|
|
return 0;
|
|
}
|
|
switch(type){
|
|
case 1: return getNextPoint();
|
|
case 3: return getNextLine();
|
|
case 5: return getNextPolygon();
|
|
case 8: return getNextMultiPoint();
|
|
default: return 0;
|
|
}
|
|
}
|
|
void close(){
|
|
if(defined){
|
|
file.close();
|
|
defined = false;
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
bool defined;
|
|
ifstream file;
|
|
string filename;
|
|
uint32_t type;
|
|
streampos fileend;
|
|
char* buffer;
|
|
|
|
bool readHeader(unsigned int allowedType){
|
|
file.seekg(0,ios::end);
|
|
streampos p = file.tellg();
|
|
fileend = p;
|
|
if(p< 100){ // minimum size not reached
|
|
return false;
|
|
}
|
|
file.seekg(0,ios::beg);
|
|
uint32_t code;
|
|
uint32_t version;
|
|
file.read(reinterpret_cast<char*>(&code),4);
|
|
file.seekg(28,ios::beg);
|
|
file.read(reinterpret_cast<char*>(&version),4);
|
|
file.read(reinterpret_cast<char*>(&type),4);
|
|
if(WinUnix::isLittleEndian()){
|
|
code = WinUnix::convertEndian(code);
|
|
} else {
|
|
version = WinUnix::convertEndian(version);
|
|
type = WinUnix::convertEndian(type);
|
|
}
|
|
if(code!=9994){
|
|
return false;
|
|
}
|
|
if(version != 1000){
|
|
return false;
|
|
}
|
|
return type==allowedType;
|
|
}
|
|
|
|
uint32_t readBigInt32(){
|
|
uint32_t res;
|
|
file.read(reinterpret_cast<char*>(&res),4);
|
|
if(WinUnix::isLittleEndian()){
|
|
res = WinUnix::convertEndian(res);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
uint32_t readLittleInt32(){
|
|
uint32_t res;
|
|
file.read(reinterpret_cast<char*>(&res),4);
|
|
if(!WinUnix::isLittleEndian()){
|
|
res = WinUnix::convertEndian(res);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
double readLittleDouble(){
|
|
uint64_t tmp;
|
|
file.read(reinterpret_cast<char*>(&tmp),8);
|
|
if(!WinUnix::isLittleEndian()){
|
|
tmp = WinUnix::convertEndian(tmp);
|
|
}
|
|
void* tmpv = (void*) &tmp;
|
|
double res = * (reinterpret_cast<double*>(tmpv));
|
|
return res;
|
|
}
|
|
|
|
Attribute* getNextPoint(){
|
|
if(file.tellg() == fileend){
|
|
return 0;
|
|
}
|
|
// uint32_t recNo =
|
|
readBigInt32();
|
|
uint32_t recLen = readBigInt32();
|
|
uint32_t type = readLittleInt32();
|
|
if(type == 0){ // null shape
|
|
if(recLen!=2 || !file.good()){
|
|
cerr << "Error in shape file detected " << __LINE__ << endl;
|
|
defined = false;
|
|
file.close();
|
|
return 0;
|
|
} else {
|
|
return new Point(false,0,0);
|
|
}
|
|
}
|
|
if(type != 1 || recLen != 10){
|
|
cerr << "Error in shape file detected " << __LINE__ << endl;
|
|
defined = false;
|
|
file.close();
|
|
return 0;
|
|
}
|
|
double x = readLittleDouble();
|
|
double y = readLittleDouble();
|
|
if(!file.good()){
|
|
cerr << "Error in shape file detected " << __LINE__ << endl;
|
|
defined = false;
|
|
file.close();
|
|
return 0;
|
|
}
|
|
return new Point(true,x,y);
|
|
}
|
|
|
|
Attribute* getNextMultiPoint(){
|
|
if(file.tellg()==fileend){
|
|
return 0;
|
|
}
|
|
uint32_t recNo = 0;
|
|
recNo = readBigInt32();
|
|
uint32_t len = 0;
|
|
len = readBigInt32();
|
|
uint32_t type = 0;
|
|
type = readLittleInt32();
|
|
|
|
if(!file.good()){
|
|
cerr << " problem in reading file " << __LINE__ << endl;
|
|
cerr << "recNo = " << recNo << endl;
|
|
cerr << "len = " << len << endl;
|
|
cerr << "type = " << type << endl;
|
|
defined = false;
|
|
return 0;
|
|
}
|
|
|
|
if(type==0){
|
|
if(len!=2){
|
|
cerr << "Error in shape file detected " << __LINE__ << endl;
|
|
defined = false;
|
|
file.close();
|
|
return 0;
|
|
} else {
|
|
return new Points(1);
|
|
}
|
|
}
|
|
if(type!=8){
|
|
cerr << "Error in shape file detected " << __LINE__ << endl;
|
|
cout << "type = " << type << endl;
|
|
cout << "file.good = " << file.good() << endl;
|
|
defined = false;
|
|
file.close();
|
|
return 0;
|
|
}
|
|
// ignore Bounding box
|
|
readLittleDouble();
|
|
readLittleDouble();
|
|
readLittleDouble();
|
|
readLittleDouble();
|
|
uint32_t numPoints = readLittleInt32();
|
|
|
|
uint32_t expectedLen = (40 + numPoints*16) / 2;
|
|
if(len != (expectedLen)){
|
|
cerr << "Error in file " << __LINE__ << endl;
|
|
cerr << "len = " << len << endl;
|
|
cerr << "numPoints " << numPoints << endl;
|
|
cerr << " expected" << expectedLen << endl;
|
|
file.close();
|
|
defined = false;
|
|
return 0;
|
|
}
|
|
Points* ps = new Points(numPoints);
|
|
Point p;
|
|
ps->StartBulkLoad();
|
|
for(unsigned int i=0;i<numPoints && file.good();i++){
|
|
double x = readLittleDouble();
|
|
double y = readLittleDouble();
|
|
p.Set(x,y);
|
|
(*ps) += p;
|
|
}
|
|
ps->EndBulkLoad();
|
|
if(!file.good()){
|
|
cerr << "Error in file " << __LINE__ << endl;
|
|
delete ps;
|
|
return 0;
|
|
} else {
|
|
return ps;
|
|
}
|
|
}
|
|
|
|
Attribute* getNextLine(){
|
|
if(file.tellg()==fileend){
|
|
return 0;
|
|
}
|
|
//uint32_t recNo =
|
|
readBigInt32();
|
|
uint32_t len = readBigInt32();
|
|
uint32_t type = readLittleInt32();
|
|
if(type==0){
|
|
if(len!=2){
|
|
cerr << "SHPIMPORT:: Error in file detected"
|
|
<< filename << endl;
|
|
file.close();
|
|
defined = false;
|
|
return 0;
|
|
} else {
|
|
return new Line(1);
|
|
}
|
|
}
|
|
// a non-null line
|
|
if(type!=3){
|
|
cerr << "Error in file detected" << endl;
|
|
file.close();
|
|
defined = false;
|
|
return 0;
|
|
}
|
|
// ignore box
|
|
readLittleDouble();
|
|
readLittleDouble();
|
|
readLittleDouble();
|
|
readLittleDouble();
|
|
|
|
uint32_t numParts = readLittleInt32();
|
|
uint32_t numPoints = readLittleInt32();
|
|
vector<int> parts;
|
|
for(unsigned int i=0;i<numParts && file.good() ; i++){
|
|
uint32_t part = readLittleInt32();
|
|
parts.push_back(part);
|
|
}
|
|
if(!file.good()){
|
|
cerr << "SHPIMPORT:: error in reading file" << filename << endl;
|
|
file.close();
|
|
defined = false;
|
|
return 0;
|
|
}
|
|
Point p1;
|
|
Point p2;
|
|
Line* line = new Line(numPoints);
|
|
int rpoints = 0;
|
|
int lastPoint = 0;
|
|
line->StartBulkLoad();
|
|
int edgeno = -1;
|
|
HalfSegment hs;
|
|
for(unsigned int i=0;i<parts.size() && file.good(); i++){
|
|
lastPoint = i==parts.size()-1?numPoints:parts[i+1];
|
|
int start = rpoints;
|
|
for(int j=start; j<lastPoint && file.good() ;j++){
|
|
double x = readLittleDouble();
|
|
double y = readLittleDouble();
|
|
p2.Set(x,y);
|
|
if(j>start){
|
|
if(!AlmostEqual(p1,p2)){
|
|
hs.Set( true, p1, p2 );
|
|
hs.attr.edgeno = ++edgeno;
|
|
(*line) += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
(*line) += hs;
|
|
}
|
|
}
|
|
p1 = p2;
|
|
rpoints++;
|
|
}
|
|
}
|
|
line->EndBulkLoad(true,true,true);//sort, realminize, robust
|
|
if(!file.good()){
|
|
cerr << "SHPIMPORT: Error in reading file " << filename << endl;
|
|
delete line;
|
|
file.close();
|
|
defined = false;
|
|
return 0;
|
|
}
|
|
return line;
|
|
}
|
|
|
|
|
|
/*
|
|
~getNextPolygon~
|
|
|
|
This function read the next region value from the file and returns it.
|
|
In case of an error or if no more regions are available, the return value
|
|
will be 0.
|
|
|
|
*/
|
|
|
|
|
|
Attribute* getNextPolygon(){
|
|
if(file.tellg()==fileend){ // end of file reached
|
|
return 0;
|
|
}
|
|
|
|
readBigInt32(); // ignore record number
|
|
uint32_t len = readBigInt32();
|
|
uint32_t type = readLittleInt32();
|
|
if(type==0){
|
|
if(len!=2){
|
|
cerr << "Error in file detected" << __LINE__ << endl;
|
|
file.close();
|
|
defined = false;
|
|
return 0;
|
|
} else { // NULL shape, return an empty region
|
|
cout << "found null shape" << endl;
|
|
return new Region(0);
|
|
}
|
|
}
|
|
if(type!=5){ // different shapes are not allowed
|
|
cerr << "Error in file detected" << __LINE__ << endl;
|
|
cerr << "Expected Type = 5, but got type: " << type << endl;
|
|
file.close();
|
|
defined = false;
|
|
return 0;
|
|
}
|
|
// ignore box
|
|
readLittleDouble();
|
|
readLittleDouble();
|
|
readLittleDouble();
|
|
readLittleDouble();
|
|
uint32_t numParts = readLittleInt32();
|
|
uint32_t numPoints = readLittleInt32();
|
|
// for debugging the file
|
|
uint32_t clen = (44 + 4*numParts + 16*numPoints)/2;
|
|
|
|
//cout << "region has " << numParts << " parts " << endl;
|
|
|
|
if(clen!=len){
|
|
cerr << "File invalid: length given in header seems to be wrong"
|
|
<< endl;
|
|
file.close();
|
|
defined = false;
|
|
return 0;
|
|
}
|
|
// read the starts of the cycles
|
|
vector<uint32_t> parts;
|
|
for(unsigned int i=0;i<numParts;i++){
|
|
uint32_t p = readLittleInt32();
|
|
parts.push_back(p);
|
|
}
|
|
|
|
|
|
// read the cycles
|
|
vector<vector <Point> > cycles;
|
|
uint32_t pos = 0;
|
|
for(unsigned int p=0;p<parts.size(); p++){
|
|
vector<Point> cycle;
|
|
uint32_t start = pos;
|
|
uint32_t end = p< (parts.size()-1) ? parts[p+1]:numPoints;
|
|
Point lastPoint(true,0.0,0.0);
|
|
// read a single cycle
|
|
for(unsigned int c=start; c< end; c++){
|
|
double x = readLittleDouble();
|
|
double y = readLittleDouble();
|
|
Point point(true,x,y);
|
|
if(c==start){ // the first point
|
|
cycle.push_back(point);
|
|
lastPoint = point;
|
|
} else if(!AlmostEqual(lastPoint,point)){
|
|
cycle.push_back(point);
|
|
lastPoint = point;
|
|
}
|
|
pos++;
|
|
}
|
|
//cout << "part " << p << " has " << cycle.size() << " points " << endl;
|
|
cycles.push_back(cycle);
|
|
}
|
|
try{
|
|
return buildRegion2(cycles);
|
|
} catch(const string& error){
|
|
cout << "error occured while creating a polygon" << endl;
|
|
cout << "Region should be created from " << cycles.size() << " cycles"
|
|
<< endl;
|
|
Region* res = new Region(0);
|
|
res->SetDefined(false);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
|
|
|
|
/*
|
|
~numOfNeighbours~
|
|
|
|
Returns the number of halfsegments having the same dominating point
|
|
like the halfsegment at positition pos (exclusive that segment).
|
|
The DbArray has to be sorted.
|
|
|
|
*/
|
|
int numOfNeighbours1(const DbArray<HalfSegment>& segs,const int pos){
|
|
HalfSegment hs1;
|
|
HalfSegment hs2;
|
|
segs.Get(pos,&hs1);
|
|
Point dp(hs1.GetDomPoint());
|
|
int num = 0;
|
|
bool done= false;
|
|
int pos2 = pos-1;
|
|
while(pos2>0 && !done){
|
|
segs.Get(pos2,hs2);
|
|
if(AlmostEqual(dp,hs2.GetDomPoint())){
|
|
num++;
|
|
pos2--;
|
|
}else {
|
|
done = true;
|
|
}
|
|
}
|
|
done = false;
|
|
pos2 = pos+1;
|
|
while(!done && pos2<segs.Size()){
|
|
segs.Get(pos2,hs2);
|
|
if(AlmostEqual(dp,hs2.GetDomPoint())){
|
|
num++;
|
|
pos2++;
|
|
}else {
|
|
done = true;
|
|
}
|
|
}
|
|
return num;
|
|
}
|
|
// slower implementation
|
|
int numOfNeighbours(const DbArray<HalfSegment>& segs,const int pos){
|
|
HalfSegment hs1;
|
|
HalfSegment hs2;
|
|
segs.Get(pos,hs1);
|
|
Point dp = hs1.GetDomPoint();
|
|
double dpx = dp.GetX();
|
|
int num = 0;
|
|
bool done= false;
|
|
int pos2 = pos-1;
|
|
while(pos2>=0 && !done){
|
|
segs.Get(pos2,hs2);
|
|
if(AlmostEqual(dp,hs2.GetDomPoint())){
|
|
num++;
|
|
pos2--;
|
|
}else {
|
|
double dpx2 = hs2.GetDomPoint().GetX();
|
|
if(AlmostEqual(dpx,dpx2)){
|
|
pos2--;
|
|
}else{
|
|
done = true;
|
|
}
|
|
}
|
|
}
|
|
done = false;
|
|
pos2 = pos+1;
|
|
while(!done && pos2<segs.Size()){
|
|
segs.Get(pos2,hs2);
|
|
if(AlmostEqual(dp,hs2.GetDomPoint())){
|
|
num++;
|
|
pos2++;
|
|
}else {
|
|
double dpx2 = hs2.GetDomPoint().GetX();
|
|
if(AlmostEqual(dpx,dpx2)){
|
|
pos2++;
|
|
} else {
|
|
done = true;
|
|
}
|
|
}
|
|
}
|
|
return num;
|
|
}
|
|
|
|
/*
|
|
~numOfUnusedNeighbours~
|
|
|
|
Returns the number of halfsegments having the same dominating point
|
|
like the halfsegment at positition pos (exclusive that segment).
|
|
The DBArray has to be sorted.
|
|
|
|
*/
|
|
int numOfUnusedNeighbours(const DbArray<HalfSegment>& segs,
|
|
const int pos,
|
|
const bool* used){
|
|
HalfSegment hs1;
|
|
HalfSegment hs2;
|
|
segs.Get(pos,hs1);
|
|
Point dp = hs1.GetDomPoint();
|
|
double dpx = dp.GetX();
|
|
int num = 0;
|
|
bool done= false;
|
|
int pos2 = pos-1;
|
|
while(pos2>0 && !done){
|
|
segs.Get(pos2,hs2);
|
|
if(AlmostEqual(dp,hs2.GetDomPoint())){
|
|
if(!used[pos2]){
|
|
num++;
|
|
}
|
|
pos2--;
|
|
}else {
|
|
double dpx2 = hs2.GetDomPoint().GetX();
|
|
if(AlmostEqual(dpx,dpx2)){
|
|
pos2--;
|
|
} else {
|
|
done = true;
|
|
}
|
|
}
|
|
}
|
|
done = false;
|
|
pos2 = pos+1;
|
|
while(!done && pos2<segs.Size()){
|
|
segs.Get(pos2,hs2);
|
|
if(AlmostEqual(dp,hs2.GetDomPoint())){
|
|
if(!used[pos2]){
|
|
num++;
|
|
}
|
|
pos2++;
|
|
}else {
|
|
double dpx2 = hs2.GetDomPoint().GetX();
|
|
if(AlmostEqual(dpx,dpx2)){
|
|
pos2++;
|
|
} else {
|
|
done = true;
|
|
}
|
|
}
|
|
}
|
|
return num;
|
|
}
|
|
|
|
|
|
void removeDeadEnd(DbArray<HalfSegment>* segments,
|
|
vector<Point>& path, vector<int>& positions,
|
|
const bool* used){
|
|
|
|
int pos = positions.size()-1;
|
|
while((pos>=0) &&
|
|
(numOfUnusedNeighbours(*segments,positions[pos],used) < 1)){
|
|
pos--;
|
|
}
|
|
if(pos<=0){ // no extension found
|
|
positions.clear();
|
|
path.clear();
|
|
} else {
|
|
cout << "Remove path from " << pos << " until its end" << endl;
|
|
positions.erase(positions.begin()+pos, positions.end());
|
|
path.erase(path.begin()+pos,path.end());
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<int filePos>
|
|
int shpimportVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
switch(message){
|
|
case OPEN: {
|
|
if(local.addr){
|
|
delete (shpimportInfo*)local.addr;
|
|
}
|
|
FText* fname = static_cast<FText*>(args[filePos].addr);
|
|
ListExpr type = nl->Second(qp->GetType(s));
|
|
local.setAddr(new shpimportInfo(type,fname));
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
if(!local.addr){
|
|
return CANCEL;
|
|
}
|
|
shpimportInfo* info = static_cast<shpimportInfo*>(local.addr);
|
|
if(!info){
|
|
return CANCEL;
|
|
}
|
|
Attribute* next = info->getNext();
|
|
result.addr = next;
|
|
return next?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
shpimportInfo* info = static_cast<shpimportInfo*>(local.addr);
|
|
if(info){
|
|
info->close();
|
|
delete info;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
default: {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
5.3 Specification
|
|
|
|
*/
|
|
const string shpimportSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>T x text -> stream(T), T in {point, points, line, region}"
|
|
"</text--->"
|
|
"<text> _ shpimport [ _ ] </text--->"
|
|
"<text>Produces a stream of spatial objects of type T from a shapefile. "
|
|
"The first argument is a dummy parameter. It is required to determine the "
|
|
"type T of the result stream.</text--->"
|
|
"<text> not tested !!!</text--->"
|
|
") )";
|
|
|
|
/*
|
|
5.6 Operator instance
|
|
|
|
*/
|
|
Operator shpimport( "shpimport",
|
|
shpimportSpec,
|
|
shpimportVM<1>,
|
|
Operator::SimpleSelect,
|
|
shpimportTM);
|
|
|
|
|
|
/*
|
|
6 Operator shpimport2
|
|
|
|
imports a shape file without given the type directly.
|
|
|
|
*/
|
|
ListExpr shpimport2TM(ListExpr args){
|
|
if(nl->ListLength(args)!=1){
|
|
return listutils::typeError("one argument expected");
|
|
}
|
|
ListExpr arg = nl->First(args);
|
|
if(nl->ListLength(arg) !=2){
|
|
return listutils::typeError("Error, argument has to consists of 2 parts");
|
|
}
|
|
ListExpr type = nl->First(arg);
|
|
ListExpr value = nl->Second(arg);
|
|
|
|
if(!listutils::isSymbol(type,FText::BasicType())){
|
|
return listutils::typeError("text expected");
|
|
}
|
|
|
|
// get the value if possible
|
|
|
|
Word res;
|
|
bool success = QueryProcessor::ExecuteQuery(nl->ToString(value),res);
|
|
if(!success){
|
|
return listutils::typeError("could not evaluate the value of " +
|
|
nl->ToString(value) );
|
|
}
|
|
|
|
FText* resText = static_cast<FText*>(res.addr);
|
|
|
|
if(!resText->IsDefined()){
|
|
resText->DeleteIfAllowed();
|
|
return listutils::typeError("filename evaluated to be undefined");
|
|
}
|
|
|
|
string name = resText->GetValue();
|
|
resText->DeleteIfAllowed();
|
|
|
|
string shpType;
|
|
bool correct;
|
|
string errmsg;
|
|
|
|
shpType = getShpType(name, correct, errmsg);
|
|
if(!correct){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->SymbolAtom(shpType));
|
|
}
|
|
|
|
|
|
|
|
const string shpimport2Spec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> text -> stream(T), T in {point, points, line, region}</text--->"
|
|
"<text>shpimport2(_) </text--->"
|
|
"<text>Produces a stream of spatial objects from a shapefile. The spatial "
|
|
"result stream element type T is determined automatically by inspecting "
|
|
"the import file."
|
|
"</text--->"
|
|
"<text> query shpimport2('kinos.shp') count</text--->"
|
|
") )";
|
|
|
|
|
|
/*
|
|
5.6 Operator instance
|
|
|
|
*/
|
|
Operator shpimport2( "shpimport2",
|
|
shpimport2Spec,
|
|
shpimportVM<0>,
|
|
Operator::SimpleSelect,
|
|
shpimport2TM);
|
|
|
|
|
|
|
|
/*
|
|
6 Operator dbtype
|
|
|
|
6.1 Type Mapping
|
|
|
|
text -> text
|
|
|
|
*/
|
|
|
|
ListExpr dbtypeTM(ListExpr args){
|
|
if(nl->ListLength(args)==1 &&
|
|
nl->IsEqual(nl->First(args),FText::BasicType())){
|
|
return nl->SymbolAtom(FText::BasicType());
|
|
}
|
|
ErrorReporter::ReportError("text expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
/*
|
|
6.2 Value Mapping
|
|
|
|
*/
|
|
|
|
ListExpr getDBAttrList(string name, bool& correct, string& errorMessage){
|
|
|
|
ifstream f;
|
|
correct = true;
|
|
f.open(name.c_str(),ios::binary);
|
|
if(!f.good()){
|
|
errorMessage="could not open file";
|
|
correct = false;
|
|
return listutils::typeError();
|
|
}
|
|
|
|
unsigned char code= 0;
|
|
f.read(reinterpret_cast<char*>(&code),1);
|
|
if(code!=0x03 && code!=0x83){
|
|
f.close();
|
|
errorMessage = "Not a DBase III file";
|
|
correct = false;
|
|
return listutils::typeError();
|
|
}
|
|
if(!f.good()){
|
|
errorMessage = "problem in reading file";
|
|
correct = false;
|
|
return listutils::typeError();
|
|
}
|
|
f.seekg(8,ios::beg);
|
|
|
|
if(!f.good()){
|
|
errorMessage = "problem in reading file";
|
|
correct = false;
|
|
return listutils::typeError();
|
|
}
|
|
|
|
uint16_t headerlength;
|
|
f.read(reinterpret_cast<char*>(&headerlength),2);
|
|
|
|
if(!WinUnix::isLittleEndian()){
|
|
headerlength = WinUnix::convertEndian(headerlength);
|
|
}
|
|
int check = (headerlength-1) % 32;
|
|
if(check!=0){
|
|
errorMessage = "wrong header length";
|
|
correct = false;
|
|
f.close();
|
|
return listutils::typeError();
|
|
}
|
|
if(!f.good()){
|
|
errorMessage = "problem in reading file";
|
|
f.close();
|
|
correct = false;
|
|
return listutils::typeError();
|
|
}
|
|
int noRecords = (headerlength-32) / 32;
|
|
|
|
|
|
f.seekg(0,ios::end);
|
|
if(f.tellg() < headerlength){
|
|
errorMessage = "invalid filesize";
|
|
correct = false;
|
|
f.close();
|
|
return listutils::typeError();
|
|
}
|
|
|
|
f.seekg(32,ios::beg);
|
|
char buffer[32];
|
|
|
|
ListExpr attrList=nl->TheEmptyList();
|
|
ListExpr last = nl->TheEmptyList();
|
|
for(int i=0;i<noRecords;i++){
|
|
f.read(buffer,32);
|
|
stringstream ns;
|
|
for(int j=0;j<11;j++){
|
|
if(buffer[j]){
|
|
ns << buffer[j];
|
|
}
|
|
}
|
|
string name = ns.str();
|
|
name[0] = toupper((unsigned char) name[0]);
|
|
unsigned char typeCode = buffer[11];
|
|
unsigned char dc = buffer[17];
|
|
unsigned char len = buffer[16];
|
|
string type = "unknown";
|
|
|
|
switch(typeCode){
|
|
case 'C' : {
|
|
type = len <= MAX_STRINGSIZE?CcString::BasicType():FText::BasicType();
|
|
break;
|
|
}
|
|
case 'D' : {
|
|
type = datetime::DateTime::BasicType();
|
|
break;
|
|
}
|
|
case 'L' : {
|
|
type = CcBool::BasicType();
|
|
break;
|
|
}
|
|
case 'M' : {
|
|
type = FText::BasicType();
|
|
break;
|
|
}
|
|
case 'N' : {
|
|
type = dc==0?CcInt::BasicType():CcReal::BasicType();
|
|
break;
|
|
}
|
|
case 'F' : {
|
|
type = CcReal::BasicType();
|
|
break;
|
|
}
|
|
default : {
|
|
errorMessage = "unknown type ";
|
|
f.close();
|
|
correct = false;
|
|
return listutils::typeError();
|
|
}
|
|
}
|
|
|
|
ListExpr attr = nl->TwoElemList(nl->SymbolAtom(name),
|
|
nl->SymbolAtom(type));
|
|
if(i==0){ // first elem
|
|
attrList = nl->OneElemList(attr);
|
|
last = attrList;
|
|
} else {
|
|
last = nl->Append(last,attr);
|
|
}
|
|
}
|
|
f.close();
|
|
return attrList;
|
|
}
|
|
|
|
|
|
|
|
int dbtypeVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
FText* arg = static_cast<FText*>(args[0].addr);
|
|
result = qp->ResultStorage(s);
|
|
FText* res = static_cast<FText*>(result.addr);
|
|
if(!arg->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
|
|
string name = arg->GetValue();
|
|
|
|
bool correct;
|
|
string errorMessage = "";
|
|
ListExpr attrList = getDBAttrList(name, correct, errorMessage);
|
|
|
|
|
|
|
|
if(!correct){
|
|
res->SetDefined(false);
|
|
cout << errorMessage << endl;
|
|
return 0;
|
|
}
|
|
|
|
stringstream resstr;
|
|
resstr << "[const rel(tuple([";
|
|
bool first = true;
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr a = nl->First(attrList);
|
|
if(!first){
|
|
resstr << ", ";
|
|
}
|
|
resstr << nl->SymbolValue(nl->First(a));
|
|
resstr << " : ";
|
|
resstr << nl->SymbolValue(nl->Second(a));
|
|
attrList = nl->Rest(attrList);
|
|
first = false;
|
|
}
|
|
resstr << "])) value ()]";
|
|
|
|
resstr.flush();
|
|
res->Set(true, resstr.str());
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
6.3 Specification
|
|
|
|
*/
|
|
|
|
const string dbtypeSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>text -> text</text--->"
|
|
"<text>dbtype( FileName ) </text--->"
|
|
"<text>Returns an object description of the secondo object"
|
|
" stored within the dbase III file specified by FileName as a text value."
|
|
"</text--->"
|
|
"<text> not tested !!!</text--->"
|
|
") )";
|
|
|
|
/*
|
|
6.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator dbtype( "dbtype",
|
|
dbtypeSpec,
|
|
dbtypeVM,
|
|
Operator::SimpleSelect,
|
|
dbtypeTM);
|
|
|
|
|
|
/*
|
|
7 Operator dbimport
|
|
|
|
7.1 Type Mapping
|
|
|
|
(rel(tuple(...))) x text -> stream(tuple(...)
|
|
|
|
*/
|
|
ListExpr dbimportTM(ListExpr args){
|
|
if(nl->ListLength(args)!=2){
|
|
ErrorReporter::ReportError("rel x text expected");
|
|
return nl->TypeError();
|
|
}
|
|
if(!IsRelDescription(nl->First(args)) ||
|
|
!nl->IsEqual(nl->Second(args),FText::BasicType())){
|
|
ErrorReporter::ReportError("rel x text expected");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr res = nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->Second(nl->First(args)));
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
7.2 Specification
|
|
|
|
*/
|
|
|
|
const string dbimportSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>rel(tuple(...) x text -> stream(tuple(...))</text--->"
|
|
"<text>Rel dbimport[ FileName ] </text--->"
|
|
"<text>Returns the content of the file as a tuple stream. The result tuple "
|
|
"type must the passed as a relation Rel of the same tupletype as the "
|
|
"result stream.</text--->"
|
|
"<text> not tested !!!</text--->"
|
|
") )";
|
|
|
|
|
|
/*
|
|
7.3 Value Mapping
|
|
|
|
*/
|
|
class DbimportInfo{
|
|
|
|
public:
|
|
|
|
/*
|
|
~Constructor~
|
|
|
|
*/
|
|
DbimportInfo(ListExpr type, FText* fname){
|
|
BasicTuple = 0;
|
|
tupleType = 0;
|
|
buffer1 = new char[FILE_BUFFER_SIZE];
|
|
buffer2 = new char[FILE_BUFFER_SIZE];
|
|
|
|
ListExpr attrList = nl->Second(nl->Second(type));
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr type = nl->Second(nl->First(attrList));
|
|
if(nl->AtomType(type)!=SymbolType){
|
|
cerr << " composite type not allowed " << endl;
|
|
defined = false;
|
|
return;
|
|
}
|
|
types.push_back(nl->SymbolValue(type));
|
|
attrList = nl->Rest(attrList);
|
|
}
|
|
|
|
if( !fname->IsDefined()){
|
|
cerr << " undefined filename" << endl;
|
|
defined = false;
|
|
return;
|
|
}
|
|
name = fname->GetValue();
|
|
file.open((name).c_str(),ios::binary);
|
|
file.rdbuf()->pubsetbuf(buffer1,FILE_BUFFER_SIZE);
|
|
if(!file.good()) {
|
|
cerr << "DBIMPORT: error in reading file (open failed)"
|
|
<< name << endl;
|
|
defined = false;
|
|
return;
|
|
}
|
|
if(!checkFile()){
|
|
cerr << "CheckFile failed" << endl;
|
|
defined = false;
|
|
return;
|
|
}
|
|
defined = true;
|
|
ListExpr numType = nl->Second(
|
|
SecondoSystem::GetCatalog()->NumericType((type)));
|
|
tupleType = new TupleType(numType);
|
|
BasicTuple = new Tuple(numType);
|
|
current = 0;
|
|
file.seekg(headerLength,ios::beg);
|
|
bool found = false;
|
|
for(unsigned int i=0;i<isMemo.size() && !found;i++){
|
|
found = isMemo[i];
|
|
}
|
|
if(found){
|
|
bool endsWith = true;
|
|
if(name.size() < 4){
|
|
endsWith = false;
|
|
} else {
|
|
size_t s = name.size();
|
|
endsWith = (name[s-4] == '.') &&
|
|
((name[s-3]=='d') || (name[s-3]=='D') ) &&
|
|
((name[s-2]=='b') || (name[s-2]=='B') ) &&
|
|
((name[s-1]=='f') || (name[s-1]=='F') );
|
|
}
|
|
string memoname;
|
|
if(endsWith){
|
|
memoname = name;
|
|
size_t p = name.size()-1;
|
|
if(memoname[p] == 'f'){
|
|
memoname[p] = 't';
|
|
} else {
|
|
memoname[p] = 'T';
|
|
}
|
|
} else {
|
|
memoname = name +".dbt";
|
|
}
|
|
memofile.open(memoname.c_str(),ios::binary);
|
|
memofile.rdbuf()->pubsetbuf(buffer2,FILE_BUFFER_SIZE);
|
|
if(!memofile.good()){
|
|
cerr << "cannot open dbt file " + memoname << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
Tuple* getNext(){
|
|
if(!defined || current==noRecords || file.eof() || !file.good()){
|
|
return 0;
|
|
}
|
|
unsigned char buffer[recordSize];
|
|
// read buffer
|
|
file.read(reinterpret_cast<char*>(buffer),recordSize);
|
|
|
|
if(!file.good()){ // error in reading file, end of file reached
|
|
defined = false;
|
|
return 0;
|
|
}
|
|
Tuple* ResultTuple = BasicTuple->Clone();
|
|
|
|
unsigned int offset = 1; // ignore a deleted flag
|
|
for(int i=0;i<noAttributes;i++){
|
|
if(!store(types[i], ResultTuple, i, buffer,offset, fieldLength[i])){
|
|
delete ResultTuple;
|
|
defined = false;
|
|
return 0;
|
|
}
|
|
int fl = fieldLength[i];
|
|
offset += fl;
|
|
}
|
|
return ResultTuple;
|
|
}
|
|
|
|
~DbimportInfo(){
|
|
if(BasicTuple){
|
|
delete BasicTuple;
|
|
BasicTuple = 0;
|
|
}
|
|
if(tupleType){
|
|
tupleType->DeleteIfAllowed();
|
|
tupleType=0;
|
|
}
|
|
if(buffer1){
|
|
delete[] buffer1;
|
|
}
|
|
if(buffer2){
|
|
delete[] buffer2;
|
|
}
|
|
}
|
|
|
|
void close(){
|
|
file.close();
|
|
memofile.close();
|
|
}
|
|
|
|
private:
|
|
bool defined;
|
|
string name;
|
|
ifstream file;
|
|
ifstream memofile;
|
|
uint32_t noRecords;
|
|
uint16_t headerLength;
|
|
uint16_t recordSize;
|
|
uint16_t noAttributes;
|
|
vector<string> types;
|
|
vector<bool> isMemo;
|
|
vector<unsigned char> fieldLength;
|
|
Tuple* BasicTuple;
|
|
TupleType* tupleType;
|
|
unsigned int current;
|
|
char* buffer1;
|
|
char* buffer2;
|
|
|
|
bool checkFile(){
|
|
file.seekg(0,ios::beg);
|
|
unsigned char code;
|
|
file.read(reinterpret_cast<char*>(&code),1);
|
|
file.seekg(4,ios::beg);
|
|
if( !file.good() || ( (code!=3) && (code!=0x83) ) ){
|
|
cerr << "invalid code" << endl;
|
|
file.close();
|
|
return false;
|
|
}
|
|
file.read(reinterpret_cast<char*>(&noRecords),4);
|
|
file.read(reinterpret_cast<char*>(&headerLength),2);
|
|
file.read(reinterpret_cast<char*>(&recordSize),2);
|
|
if(!WinUnix::isLittleEndian()){
|
|
noRecords = WinUnix::convertEndian(noRecords);
|
|
headerLength = WinUnix::convertEndian(headerLength);
|
|
recordSize = WinUnix::convertEndian(recordSize);
|
|
}
|
|
file.seekg(32,ios::beg);
|
|
if(!file.good()){
|
|
cerr << "Error in reading file" << endl;
|
|
file.close();
|
|
return false;
|
|
}
|
|
if( (headerLength-1) % 32 != 0){
|
|
cerr << " invalid length for the header" << headerLength << endl;
|
|
file.close();
|
|
return false;
|
|
}
|
|
noAttributes = (headerLength - 32) / 32;
|
|
if(noAttributes < 1){
|
|
cerr << "numer of attributes invalid" << noAttributes << endl;
|
|
file.close();
|
|
return false;
|
|
}
|
|
if(noAttributes != types.size()){
|
|
cerr << "numbers of types not match "
|
|
<< types.size() << " <-> " << noAttributes << endl;
|
|
file.close();
|
|
return false;
|
|
}
|
|
unsigned char buffer[32];
|
|
for(int i=0; i < noAttributes; i++){
|
|
file.read(reinterpret_cast<char*>(buffer),32);
|
|
unsigned char t = buffer[11];
|
|
unsigned char len = buffer[16];
|
|
unsigned char dc = buffer[17];
|
|
string type;
|
|
switch(t){
|
|
case 'C' : type = len <= MAX_STRINGSIZE?CcString::BasicType()
|
|
:FText::BasicType();
|
|
isMemo.push_back(false);
|
|
break;
|
|
case 'D' : type = datetime::DateTime::BasicType();
|
|
isMemo.push_back(false);
|
|
break;
|
|
case 'L' : type = CcBool::BasicType();
|
|
isMemo.push_back(false);
|
|
break;
|
|
case 'M' : type = FText::BasicType();
|
|
isMemo.push_back(true);
|
|
if(len!=10){
|
|
cerr << "Invalid field length for memo detected ,"
|
|
<< " correct to 10" << endl;
|
|
len = 10;
|
|
}
|
|
break;
|
|
case 'N' : type = dc==0?CcInt::BasicType():CcReal::BasicType();
|
|
isMemo.push_back(false);
|
|
break;
|
|
case 'F' : type = CcReal::BasicType();
|
|
isMemo.push_back(false);
|
|
break;
|
|
default : cerr << "non supported type code:" << t << endl;
|
|
file.close();
|
|
return false;
|
|
}
|
|
if(type!=types[i]){
|
|
cerr << "non-matching types " << type << " <-> " << types[i] << endl;
|
|
cerr << "file is " << name << endl;
|
|
file.close();
|
|
return false;
|
|
} else {
|
|
fieldLength.push_back(len);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void trim(string& str) {
|
|
string::size_type pos = str.find_last_not_of(' ');
|
|
if(pos != string::npos) {
|
|
str.erase(pos + 1);
|
|
pos = str.find_first_not_of(' ');
|
|
if(pos != string::npos){
|
|
str.erase(0, pos);
|
|
}
|
|
} else {
|
|
str.erase(str.begin(), str.end());
|
|
}
|
|
}
|
|
|
|
bool store(string type, Tuple* tuple, int index,
|
|
unsigned char* buf, int offset, int length){
|
|
stringstream ss;
|
|
for(int i=offset; i<offset+length;i++){
|
|
if(buf[i]!=0){
|
|
ss << buf[i];
|
|
}
|
|
}
|
|
string s = ss.str();
|
|
if(type==CcInt::BasicType()){
|
|
if(s.size()==0){
|
|
tuple->PutAttribute(index, new CcInt(false,0));
|
|
} else {
|
|
trim(s);
|
|
istringstream buffer(s);
|
|
int res_int;
|
|
buffer >> res_int;
|
|
tuple->PutAttribute(index, new CcInt(true,res_int));
|
|
}
|
|
} else if(type==CcReal::BasicType()){
|
|
if(s.size()==0){
|
|
tuple->PutAttribute(index, new CcReal(false,0));
|
|
} else {
|
|
trim(s);
|
|
istringstream buffer(s);
|
|
double res_double;
|
|
buffer >> res_double;
|
|
tuple->PutAttribute(index, new CcReal(true,res_double));
|
|
}
|
|
} else if(type==CcString::BasicType()){
|
|
if(s.size()==0){
|
|
tuple->PutAttribute(index, new CcString(false,""));
|
|
} else {
|
|
tuple->PutAttribute(index, new CcString(true,s));
|
|
}
|
|
} else if(type==CcBool::BasicType()){
|
|
trim(s);
|
|
if(s.size()==0 || s=="?"){
|
|
tuple->PutAttribute(index, new CcBool(false,false));
|
|
}
|
|
bool res_bool = s=="y" || s=="Y" || s=="t" || s=="T";
|
|
tuple->PutAttribute(index,new CcBool(true,res_bool));
|
|
|
|
} else if(type==FText::BasicType()){
|
|
bool ismemo = isMemo[index];
|
|
if(ismemo){
|
|
if(s.size()==0){
|
|
tuple->PutAttribute(index, new FText(false,""));
|
|
} else {
|
|
trim(s);
|
|
if(s.size() ==0){
|
|
tuple->PutAttribute(index,new FText(true,""));
|
|
} else { // need access to dbt file
|
|
// compute block number
|
|
istringstream buffer(s);
|
|
int bn;
|
|
buffer >> bn;
|
|
memofile.seekg(512*bn,ios::beg);
|
|
if(memofile.good()){
|
|
char c;
|
|
stringstream text;
|
|
memofile.read(&c,1);
|
|
while(memofile.good() && c!=0x1A){
|
|
text << c;
|
|
memofile.read(&c,1);
|
|
}
|
|
if(!memofile.good()){
|
|
cerr << "Error in reading memo file";
|
|
}
|
|
tuple->PutAttribute(index,new FText(true,text.str()));
|
|
} else {
|
|
tuple->PutAttribute(index,new FText(false,""));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if(s.size()==0){
|
|
tuple->PutAttribute(index, new FText(false,""));
|
|
} else {
|
|
tuple->PutAttribute(index, new FText(true,s));
|
|
}
|
|
}
|
|
} else if(type==datetime::DateTime::BasicType()){
|
|
datetime::DateTime* res =
|
|
new datetime::DateTime(datetime::instanttype);
|
|
if(s.size()==0){
|
|
res->SetDefined(false);
|
|
} else {
|
|
|
|
istringstream buffer(s);
|
|
int resInt;
|
|
buffer >> resInt;
|
|
int year = resInt/10000;
|
|
if(year<100) {
|
|
year += 2000;
|
|
}
|
|
int month = (resInt/100)%100;
|
|
int day = resInt%100;
|
|
res->Set(year,month,day);
|
|
}
|
|
tuple->PutAttribute(index,res);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
int dbimportVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
switch(message){
|
|
case OPEN: {
|
|
ListExpr type = qp->GetType(s);
|
|
FText* fname = static_cast<FText*>(args[1].addr);
|
|
local.setAddr(new DbimportInfo(type,fname));
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
DbimportInfo* info = static_cast<DbimportInfo*>(local.addr);
|
|
if(!info){
|
|
return CANCEL;
|
|
} else {
|
|
Tuple* tuple = info->getNext();
|
|
result.addr = tuple;
|
|
return tuple==0?CANCEL:YIELD;
|
|
}
|
|
}
|
|
case CLOSE: {
|
|
DbimportInfo* info = static_cast<DbimportInfo*>(local.addr);
|
|
if(info){
|
|
info->close();
|
|
delete info;
|
|
local.addr=0;
|
|
}
|
|
|
|
}
|
|
default: {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
7.4 Operator Instance
|
|
|
|
*/
|
|
Operator dbimport( "dbimport",
|
|
dbimportSpec,
|
|
dbimportVM,
|
|
Operator::SimpleSelect,
|
|
dbimportTM);
|
|
|
|
|
|
|
|
|
|
/*
|
|
8 Operator db3import2
|
|
|
|
This variant of the db3import operator combines the dbtype and the
|
|
dbimport operator to a single operator. It exploits a new feature of
|
|
secondo, namely accessing values of constant expressions within type mappings
|
|
not available during the implementation of db3type and db3import.
|
|
|
|
*/
|
|
|
|
/*
|
|
8.1 Value Mapping
|
|
|
|
|
|
The value mapping of this function is text -> stream(tuple(....))
|
|
|
|
The tuple type depends on the content of the file specified by the text.
|
|
If the file does not reprensent a valid db3 file, the result will be
|
|
a typeerror.
|
|
|
|
*/
|
|
ListExpr dbimport2TM(ListExpr args){
|
|
if(nl->ListLength(args)!=1){
|
|
return listutils::typeError("one argument expected");
|
|
}
|
|
|
|
ListExpr arg = nl->First(args);
|
|
// the format must be (text value)
|
|
// where values represents the value of the text as
|
|
// query expression
|
|
if(nl->ListLength(arg)!=2){
|
|
return listutils::typeError("internal error");
|
|
}
|
|
|
|
if(!listutils::isSymbol(nl->First(arg), FText::BasicType())){
|
|
return listutils::typeError("the argument must be of type text");
|
|
}
|
|
|
|
ListExpr value = nl->Second(arg);
|
|
|
|
Word res;
|
|
bool success = QueryProcessor::ExecuteQuery(nl->ToString(value),res);
|
|
if(!success){
|
|
return listutils::typeError("could not evaluate the value of text");
|
|
}
|
|
|
|
FText* resText = static_cast<FText*>(res.addr);
|
|
|
|
if(!resText->IsDefined()){
|
|
resText->DeleteIfAllowed();
|
|
return listutils::typeError("filename evaluated to be undefined");
|
|
}
|
|
|
|
|
|
string name = resText->GetValue();
|
|
resText->DeleteIfAllowed();
|
|
resText = 0;
|
|
bool correct;
|
|
string errMsg="";
|
|
ListExpr attrList = getDBAttrList(name,correct, errMsg);
|
|
if(!correct){
|
|
return listutils::typeError(errMsg);
|
|
}
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom(Tuple::BasicType()),
|
|
attrList));
|
|
|
|
|
|
}
|
|
|
|
|
|
const string dbimport2Spec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> text -> stream (tuple(...))</text--->"
|
|
"<text>dbimport2( FileName) </text--->"
|
|
"<text>Returns the content of file FineName as a stream. The stream tuple "
|
|
"type is determined automatically by inspecting the file.</text--->"
|
|
"<text> </text--->"
|
|
") )";
|
|
|
|
|
|
|
|
int dbimport2VM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
switch(message){
|
|
case OPEN: {
|
|
ListExpr type = qp->GetType(s);
|
|
FText* fname = static_cast<FText*>(args[0].addr);
|
|
local.setAddr(new DbimportInfo(type,fname));
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
DbimportInfo* info = static_cast<DbimportInfo*>(local.addr);
|
|
if(!info){
|
|
return CANCEL;
|
|
} else {
|
|
Tuple* tuple = info->getNext();
|
|
result.addr = tuple;
|
|
return tuple==0?CANCEL:YIELD;
|
|
}
|
|
}
|
|
case CLOSE: {
|
|
DbimportInfo* info = static_cast<DbimportInfo*>(local.addr);
|
|
if(info){
|
|
info->close();
|
|
delete info;
|
|
local.addr=0;
|
|
}
|
|
|
|
}
|
|
default: {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Operator dbimport2( "dbimport2",
|
|
dbimport2Spec,
|
|
dbimport2VM,
|
|
Operator::SimpleSelect,
|
|
dbimport2TM);
|
|
|
|
|
|
/*
|
|
8 Operator saveObject
|
|
|
|
8.1 TypeMapping
|
|
|
|
The Type mapping is:
|
|
|
|
---- T x string x text -> bool
|
|
----
|
|
|
|
|
|
*/
|
|
|
|
ListExpr saveObjectTM(ListExpr args){
|
|
string err = "T x string x text expected";
|
|
if(nl->ListLength(args)!=3){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
if(!nl->IsEqual(nl->Second(args),CcString::BasicType()) |
|
|
!nl->IsEqual(nl->Third(args),FText::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr obj = nl->First(args);
|
|
|
|
if(!nl->IsAtom(obj) &&
|
|
!nl->IsEmpty(obj) &&
|
|
nl->IsEqual(nl->First(obj),Symbol::STREAM())){
|
|
ErrorReporter::ReportError("stream not allowes as first argument");
|
|
return nl->TypeError();
|
|
}
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
|
|
/*
|
|
8.2 Specification
|
|
|
|
*/
|
|
const string saveObjectSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> T x string x text -> bool </text--->"
|
|
"<text> _ saveObject[objName, fileName] </text--->"
|
|
"<text> saves an object to a file in nested list format</text--->"
|
|
"<text> query (3 + 4) saveObject[\"seven\",'seven.obj'] </text--->"
|
|
") )";
|
|
|
|
/*
|
|
8.3 Value Mapping
|
|
|
|
*/
|
|
|
|
int saveObjectVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
CcString* objName = static_cast<CcString*>(args[1].addr);
|
|
FText* fileName = static_cast<FText*>(args[2].addr);
|
|
if(!objName->IsDefined() || !fileName->IsDefined()){
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
ListExpr type = qp->GetType(qp->GetSon(s,0));
|
|
int algId;
|
|
int typeId;
|
|
string tname;
|
|
if(! SecondoSystem::GetCatalog()->LookUpTypeExpr(type,tname,algId,typeId)){
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
|
|
ListExpr value = (am->OutObj(algId,typeId))(type,args[0]);
|
|
|
|
string oname = objName->GetValue();
|
|
|
|
ListExpr objList = nl->FiveElemList(
|
|
nl->SymbolAtom("OBJECT"),
|
|
nl->SymbolAtom(oname),
|
|
nl->TheEmptyList(),
|
|
type,
|
|
value);
|
|
bool success = nl->WriteToFile(fileName->GetValue(),objList);
|
|
res->Set(true,success);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
8.4 Operator Instance
|
|
|
|
*/
|
|
Operator saveObject( "saveObject",
|
|
saveObjectSpec,
|
|
saveObjectVM,
|
|
Operator::SimpleSelect,
|
|
saveObjectTM);
|
|
|
|
|
|
/*
|
|
9 Operator ~isFile~
|
|
|
|
This operator checks, whether a given file exists in the filesystem.
|
|
If so, it returns ~TRUE~, otherwise ~FALSE~
|
|
|
|
9.1 Type Mapping Function ~stringORtext2boolTM~
|
|
|
|
Used for operators ~isFile~, ~removeFile~, ~isDirectory~
|
|
|
|
---- {text|string} -> bool
|
|
----
|
|
|
|
*/
|
|
ListExpr stringORtext2boolTM(ListExpr args){
|
|
string err = "text or string expected";
|
|
if(nl->ListLength(args)!=1){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
if( !nl->IsEqual(nl->First(args),CcString::BasicType())
|
|
&& !nl->IsEqual(nl->First(args),FText::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
|
|
/*
|
|
9.2 Value Mapping Function for ~checkfile~
|
|
|
|
We implement this as a template function. The template class parameter ~T~
|
|
may be either CcString or FText.
|
|
|
|
*/
|
|
|
|
template<class T>
|
|
int isFileVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
T* objName = static_cast<T*>(args[0].addr);
|
|
if(!objName->IsDefined()){
|
|
res->Set(false,false);
|
|
} else {
|
|
string fileNameS = objName->GetValue();
|
|
res->Set(true,FileSystem::FileOrFolderExists(fileNameS));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping isFilevaluemap[] = {isFileVM<CcString>, isFileVM<FText>};
|
|
|
|
|
|
/*
|
|
9.3 Specification for ~isFile~
|
|
|
|
*/
|
|
const string isFileSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {text|string} -> bool </text--->"
|
|
"<text> isFile( Name ) </text--->"
|
|
"<text> Checks, whether the file or directory Name exists.\n"
|
|
"If Name is UNDEFINED, nothing is done and the result is "
|
|
"UNDEFINED.</text--->"
|
|
"<text> query isFile('data.csv') </text--->"
|
|
") )";
|
|
|
|
/*
|
|
9.4 Selection Function for ~isFile~
|
|
|
|
*/
|
|
|
|
int stringORtextSelect( ListExpr args )
|
|
{
|
|
if(nl->IsEqual(nl->First(args),CcString::BasicType())) {
|
|
return 0;
|
|
} else if(nl->IsEqual(nl->First(args),FText::BasicType())){
|
|
return 1;
|
|
}
|
|
assert( false );
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
9.5 Operator Instance for operator ~isFile~
|
|
|
|
*/
|
|
Operator isFile ( "isFile",
|
|
isFileSpec,
|
|
2,
|
|
isFilevaluemap,
|
|
stringORtextSelect,
|
|
stringORtext2boolTM);
|
|
|
|
|
|
/*
|
|
10 Operator ~removeFile~
|
|
|
|
This operator removes/deletes a given file from the file system
|
|
|
|
10.1 Type Mapping for ~removeFile~
|
|
|
|
Uses ~stringORtext2boolTM~.
|
|
|
|
10.2 Value Mapping for ~removeFile~
|
|
|
|
*/
|
|
|
|
template<class T>
|
|
int removeFileVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
T* objName = static_cast<T*>(args[0].addr);
|
|
if(!objName->IsDefined()){
|
|
res->Set(false,false);
|
|
} else {
|
|
string fileNameS = objName->GetValue();
|
|
res->Set(true,FileSystem::DeleteFileOrFolder(fileNameS));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
ValueMapping removeFilevaluemap[] = {removeFileVM<CcString>,
|
|
removeFileVM<FText>};
|
|
|
|
/*
|
|
10.4 Selection Function for ~removeFile~
|
|
|
|
Uses ~stringORtextSelect~.
|
|
|
|
*/
|
|
|
|
/*
|
|
10.4 Specification for ~removeFile~
|
|
|
|
*/
|
|
const string removeFileSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {text|string} -> bool </text--->"
|
|
"<text> removeFile( Name ) </text--->"
|
|
"<text> Deletes the file or empty folder with the given Name from"
|
|
" the files system. Returns TRUE, if this succeeds, and FALSE if the "
|
|
"file could not be deleted or any error occurs. If the Name is\n"
|
|
"UNDEFINED, nothing is done and the result is UNDEFINED.\n"
|
|
"WARNING: Be extremely careful about removing files!</text--->"
|
|
"<text> query removeFile('data.csv') </text--->"
|
|
") )";
|
|
|
|
|
|
/*
|
|
10.5 Operator Instance for operator ~removeFile~
|
|
|
|
*/
|
|
Operator removeFile ( "removeFile",
|
|
removeFileSpec,
|
|
2,
|
|
removeFilevaluemap,
|
|
stringORtextSelect,
|
|
stringORtext2boolTM);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
10.2 Operator ~rtf2text~
|
|
|
|
This operator converts a given rtf file into a text file and
|
|
save it in txt format
|
|
|
|
10.2.1 Type Mapping for ~rtf2txtfile~
|
|
|
|
Uses ~stringORtext2boolTM~
|
|
|
|
10.2.2 Value Mapping for ~rtf2txtfile~
|
|
|
|
*/
|
|
#ifndef SECONDO_WIN32
|
|
|
|
template<class T>
|
|
int rtf2txtfileVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s)
|
|
{
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
T* objName = static_cast<T*>(args[0].addr);
|
|
|
|
if(!objName->IsDefined())
|
|
{
|
|
res->Set(false,false);
|
|
}
|
|
|
|
else {
|
|
|
|
string fileNameS = objName->GetValue();
|
|
|
|
string::size_type size;
|
|
size = fileNameS.size();
|
|
string str3 = fileNameS.substr (size-4);
|
|
|
|
if (!(str3 == ".rtf"))
|
|
{
|
|
res->Set(false,false);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
string sourcefile = fileNameS;
|
|
size_t f = fileNameS.find(".rtf");
|
|
string filetarget = sourcefile.replace(f, std::string(".rtf").length(),
|
|
".txt");
|
|
|
|
string call = "unrtf --text " + fileNameS + " > " + filetarget;
|
|
const char * cll = call.c_str();
|
|
string deltxt = "rm " + filetarget;
|
|
const char * del = deltxt.c_str();
|
|
|
|
|
|
int back = system(cll);
|
|
|
|
|
|
|
|
if (back == 0)
|
|
{
|
|
res->Set(true,true);
|
|
|
|
return 0;
|
|
} else {
|
|
res->Set(true,false);
|
|
back = system(del);
|
|
if(back != 0){
|
|
std::cerr << "Problem in deletion of temporarly file" << std::endl;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
ValueMapping rtf2txtfilevaluemap[] = {rtf2txtfileVM<CcString>,
|
|
rtf2txtfileVM<FText>};
|
|
|
|
/*
|
|
10.2.3 Selection Function for ~rtf2txtfile~
|
|
|
|
Uses ~stringORtextSelect~
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
10.2.4 Specification for ~rtf2txtfile~
|
|
|
|
*/
|
|
|
|
|
|
|
|
const string rtf2txtfileSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {text|string} -> bool </text--->"
|
|
"<text> rtf2text ( Name ) </text--->"
|
|
"<text>Converts a given rtf file and creats a txt file with the same name"
|
|
" using unrtf linux tool. Returns TRUE, if this succeeds and FALSE if the "
|
|
"file could not be converted or opened. And UNDEFINED if any "
|
|
"error occurs.</text--->"
|
|
"<text> query rtf2txtfile('text.rtf') </text--->"
|
|
") )";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
10.2.5 Operator Instance for operator ~rtf2txtfile~
|
|
|
|
*/
|
|
|
|
|
|
Operator rtf2txtfile ( "rtf2txtfile",
|
|
rtf2txtfileSpec,
|
|
2,
|
|
rtf2txtfilevaluemap,
|
|
stringORtextSelect,
|
|
stringORtext2boolTM);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
11 Operator ~createDirectory~
|
|
|
|
The operator creates the passed directory within the file system.
|
|
|
|
11.1 Type Mapping for ~createDirectory~
|
|
|
|
*/
|
|
|
|
ListExpr createDirectoryTM(ListExpr args){
|
|
string err = "{string,text} [ x bool] expected";
|
|
if(!nl->HasLength(args,2) && !nl->HasLength(args,1)){
|
|
return listutils::typeError("wrong number of arguments");
|
|
}
|
|
ListExpr arg1 = nl->First(args);
|
|
if(!FText::checkType(arg1) && !CcString::checkType(arg1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(nl->HasLength(args,2)){
|
|
if(!CcBool::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList( nl->BoolAtom(false)),
|
|
listutils::basicSymbol<CcBool>());
|
|
}
|
|
|
|
|
|
/*
|
|
11.2 Value Mapping for ~createDirectory~
|
|
|
|
*/
|
|
|
|
|
|
|
|
template<class T>
|
|
int createDirectoryVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
T* objName = static_cast<T*>(args[0].addr);
|
|
CcBool* parents = (CcBool*) args[1].addr;
|
|
if(!objName->IsDefined()|| !parents->IsDefined()) {
|
|
res->Set(false,false);
|
|
} else {
|
|
string fileNameS = objName->GetValue();
|
|
bool p = parents->GetValue();
|
|
if(p){
|
|
res->Set(true,FileSystem::CreateFolderEx(fileNameS));
|
|
} else {
|
|
res->Set(true,FileSystem::CreateFolder(fileNameS));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping createDirectoryvaluemap[] = {createDirectoryVM<CcString>,
|
|
createDirectoryVM<FText>};
|
|
|
|
/*
|
|
11.3 Specification for ~createDirectory~
|
|
|
|
*/
|
|
const string createDirectorySpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {text|string} [x bool]-> bool </text--->"
|
|
"<text> createDirectory( Name ) </text--->"
|
|
"<text> Creates a directory with the given Name on"
|
|
" the files system. Returns TRUE, if this succeeds, and FALSE if the "
|
|
"directory could not be created or any error occurs. If the Name is\n"
|
|
"UNDEFINED, nothing is done and the result is UNDEFINED. If the oprional"
|
|
"boolean argument is specified, non-existent parent directories are"
|
|
" craeted automatocally.</text--->"
|
|
"<text> query createDirectory('my_csv_directory') </text--->"
|
|
") )";
|
|
|
|
/*
|
|
11.4 Selection Function for ~createDirectory~
|
|
|
|
Uses ~stringORtextSelect~
|
|
|
|
11.5
|
|
Operator Instance for operator ~createDirectory~
|
|
|
|
*/
|
|
Operator createDirectory ( "createDirectory",
|
|
createDirectorySpec,
|
|
2,
|
|
createDirectoryvaluemap,
|
|
stringORtextSelect,
|
|
createDirectoryTM);
|
|
|
|
|
|
/*
|
|
12 Operator ~fileSize~
|
|
|
|
Returns the size of a file or directory on the file system
|
|
|
|
12.1 Type Mapping for ~fileSize~
|
|
|
|
---- {text|string} [ x bool ] -> int
|
|
----
|
|
|
|
*/
|
|
ListExpr stringORtextOPTIONbool2intTM(ListExpr args){
|
|
string err = "{text|string} [ x bool ] expected";
|
|
int listLength = nl->ListLength(args);
|
|
if(listLength!=1 && listLength !=2){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
if( !nl->IsEqual(nl->First(args),CcString::BasicType())
|
|
&& !nl->IsEqual(nl->First(args),FText::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
if (listLength == 2 && !nl->IsEqual(nl->Second(args),CcBool::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
return nl->SymbolAtom(CcInt::BasicType());
|
|
}
|
|
|
|
/*
|
|
12.2 Value Mapping for ~fileSize~
|
|
|
|
*/
|
|
|
|
template<class T>
|
|
int fileSizeVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = static_cast<CcInt*>(result.addr);
|
|
T* objName = static_cast<T*>(args[0].addr);
|
|
if(!objName->IsDefined()){
|
|
res->Set(false,0);
|
|
return 0;
|
|
}
|
|
bool recursive = false;
|
|
if(qp->GetNoSons(s)==2){
|
|
CcBool* rec = static_cast<CcBool*>(args[1].addr);
|
|
if(!rec->IsDefined()){
|
|
res->Set(false,0);
|
|
} else {
|
|
recursive = rec->GetBoolval();
|
|
}
|
|
}
|
|
if(recursive){
|
|
cerr<<"Recursive file size calculation still not implemented!\n"<<endl;
|
|
}
|
|
string fileNameS = objName->GetValue();
|
|
int32_t size = FileSystem::GetFileSize(fileNameS);
|
|
if(size<0){
|
|
res->Set(false,0);
|
|
return 0;
|
|
}
|
|
res->Set(true,size);
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping fileSizevaluemap[] = {fileSizeVM<CcString>,
|
|
fileSizeVM<FText>};
|
|
|
|
/*
|
|
12.3 Selection Function for ~fileSize~
|
|
|
|
Uses ~stringORtextSelect~.
|
|
|
|
12.3 Specification for ~fileSize~
|
|
|
|
*/
|
|
const string fileSizeSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {text|string} [ x bool ] -> bool </text--->"
|
|
"<text> fileSize( Name, Recurse ) </text--->"
|
|
"<text> Returns the file's or directory's size in Bytes.\n"
|
|
"If Name specifies a directory and the optional parameter Recurse is TRUE, "
|
|
"the operator recurses into subdirectories aggregating the total size.\n"
|
|
"If Name or Recurse is UNDEFINED, nothing is done and the result is "
|
|
"UNDEFINED.</text--->"
|
|
"<text> query fileSize('data.csv') </text--->"
|
|
") )";
|
|
|
|
/*
|
|
12.5 Operator Instance for operator ~fileSize~
|
|
|
|
*/
|
|
Operator fileSize ( "fileSize",
|
|
fileSizeSpec,
|
|
2,
|
|
fileSizevaluemap,
|
|
stringORtextSelect,
|
|
stringORtextOPTIONbool2intTM);
|
|
|
|
/*
|
|
13 Operator ~isDirectory~
|
|
|
|
This operator checks, whether a given file is a directory in the filesystem.
|
|
If so, it returns ~TRUE~, otherwise ~FALSE~
|
|
|
|
13.1 Type Mapping Function for ~isDirectory~
|
|
|
|
Uses ~stringORtext2boolTM~.
|
|
|
|
13.2 Value Mapping Function for ~isDirectory~
|
|
|
|
We implement this as a template function. The template class parameter ~T~
|
|
may be either CcString or FText.
|
|
|
|
*/
|
|
|
|
template<class T>
|
|
int isDirectoryVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
T* objName = static_cast<T*>(args[0].addr);
|
|
if(!objName->IsDefined()){
|
|
res->Set(false,false);
|
|
} else {
|
|
string fileNameS = objName->GetValue();
|
|
res->Set(true,FileSystem::IsDirectory(fileNameS));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping isDirectoryvaluemap[] = {isDirectoryVM<CcString>,
|
|
isDirectoryVM<FText>};
|
|
|
|
|
|
/*
|
|
13.3 Specification for ~isDirectory~
|
|
|
|
*/
|
|
const string isDirectorySpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {text|string} -> bool </text--->"
|
|
"<text> isDirectory( Name ) </text--->"
|
|
"<text> Checks, whether Name is a directory.\n"
|
|
"If Name is UNDEFINED, nothing is done and the result is "
|
|
"UNDEFINED.</text--->"
|
|
"<text> query isDirectory('data.csv') </text--->"
|
|
") )";
|
|
|
|
/*
|
|
13.4 Selection Function for ~isDirectory~
|
|
|
|
Uses ~stringORtextSelect~.
|
|
|
|
13.5 Operator Instance for operator ~isDirectory~
|
|
|
|
*/
|
|
Operator isDirectory ( "isDirectory",
|
|
isDirectorySpec,
|
|
2,
|
|
isDirectoryvaluemap,
|
|
stringORtextSelect,
|
|
stringORtext2boolTM);
|
|
|
|
|
|
/*
|
|
14 Operator ~writeFile~
|
|
|
|
The operator writes a ~text~ to a (text-) file on the file system.
|
|
|
|
14.1 Type Mapping for ~writeFile~
|
|
|
|
---- text x {text|string} [ x bool ] -> bool
|
|
----
|
|
|
|
*/
|
|
ListExpr stringORtext_stringORtext_OPTIONALbool2boolTM(ListExpr args){
|
|
string err = "{text|string} x {text|string} [ x bool ] expected";
|
|
int listLength = nl->ListLength(args);
|
|
if(listLength!=2 && listLength !=3){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
if( !nl->IsEqual(nl->First(args),CcString::BasicType())
|
|
&& !nl->IsEqual(nl->First(args),FText::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
if( !nl->IsEqual(nl->Second(args),CcString::BasicType())
|
|
&& !nl->IsEqual(nl->Second(args),FText::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
|
|
if (listLength == 3 && !nl->IsEqual(nl->Third(args),CcBool::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
|
|
|
|
/*
|
|
14.2 Value Mapping for ~writeFile~
|
|
|
|
*/
|
|
template<class T, class S>
|
|
int writeFileVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
T* content = static_cast<T*>(args[0].addr);
|
|
S* fileName = static_cast<S*>(args[1].addr);
|
|
|
|
if(!content->IsDefined() || !fileName->IsDefined()){
|
|
res->Set(false,false);
|
|
return 0;
|
|
}
|
|
string fileNameS = fileName->GetValue();
|
|
if(fileNameS == ""){
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
bool append = false;
|
|
if(qp->GetNoSons(s)==3){
|
|
CcBool* app = static_cast<CcBool*>(args[2].addr);
|
|
if(!app->IsDefined()){
|
|
res->Set(false,false);
|
|
return 0;
|
|
} else {
|
|
append = app->GetBoolval();
|
|
}
|
|
}
|
|
bool fileExists = FileSystem::FileOrFolderExists(fileNameS);
|
|
bool fileIsDirectory = FileSystem::IsDirectory(fileNameS);
|
|
if(fileExists && fileIsDirectory){
|
|
cerr << "writeFile: Cannot write to existing directory!\n"<<endl;
|
|
res->Set(false,false);
|
|
return 0;
|
|
}
|
|
string contentS = content->GetValue();
|
|
ofstream file;
|
|
if(append){
|
|
file.open(fileNameS.c_str(), ios::out | ios::app | ios_base::binary);
|
|
} else {
|
|
file.open(fileNameS.c_str(), ios::out | ios::trunc | ios_base::binary);
|
|
}
|
|
if(!file.good()){
|
|
cerr << "writeFile: Cannot open file '"<< fileName << "'!\n"<<endl;
|
|
res->Set(false,false);
|
|
return 0;
|
|
}
|
|
file << contentS;
|
|
if(file.good()){
|
|
res->Set(true,true);
|
|
} else {
|
|
res->Set(true,false);
|
|
}
|
|
file.close();
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping writeFilevaluemap[] = {writeFileVM<CcString, CcString>,
|
|
writeFileVM<CcString, FText >,
|
|
writeFileVM<FText, CcString>,
|
|
writeFileVM<FText, FText >};
|
|
|
|
|
|
|
|
/*
|
|
14.3 Specification for ~writeFile~
|
|
|
|
*/
|
|
const string writeFileSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {text|string} x {text|string} [ x bool ] -> bool </text--->"
|
|
"<text> writeFile( Content, Name, Append ) </text--->"
|
|
"<text> Writes text Content to file Name. If the file does not exist, it\n"
|
|
"is created. If optional argument Append = TRUE, Content will be appended\n"
|
|
"to the file. Otherwise the file gets possibly overwritten.\n"
|
|
"If any argument is UNDEFINED, nothing is done and the result is "
|
|
"UNDEFINED. If writing succeeds, result is TRUE.</text--->"
|
|
"<text> query writeFile('This is content', 'data.csv') </text--->"
|
|
") )";
|
|
|
|
/*
|
|
14.4 Selection Function for ~writeFile~
|
|
|
|
*/
|
|
int stringORtext_stringORtext_Select( ListExpr args )
|
|
{
|
|
int index = 0;
|
|
// first arg
|
|
if(nl->IsEqual(nl->First(args),CcString::BasicType())) {
|
|
index += 0;
|
|
} else if(nl->IsEqual(nl->First(args),FText::BasicType())){
|
|
index += 2;
|
|
} else {
|
|
assert(false);
|
|
return -1;
|
|
}
|
|
// second arg
|
|
if(nl->IsEqual(nl->Second(args),CcString::BasicType())) {
|
|
index += 0;
|
|
} else if(nl->IsEqual(nl->Second(args),FText::BasicType())){
|
|
index += 1;
|
|
} else {
|
|
assert(false);
|
|
return -1;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
|
|
/*
|
|
14.5 Operator Instance for operator ~isDirectory~
|
|
|
|
*/
|
|
Operator writeFile ( "writeFile",
|
|
writeFileSpec,
|
|
4,
|
|
writeFilevaluemap,
|
|
stringORtext_stringORtext_Select,
|
|
stringORtext_stringORtext_OPTIONALbool2boolTM);
|
|
|
|
|
|
/*
|
|
15 Operator ~readFile~
|
|
|
|
Reads a file into a text object. If the file contains a NULL, the result is
|
|
UNDEFINED.
|
|
|
|
15.1 TypeMapping for ~readFile~
|
|
|
|
---- {text|string} -> text
|
|
----
|
|
|
|
*/
|
|
|
|
ListExpr stringORtext2textTM(ListExpr args){
|
|
string err = "{text|string} expected";
|
|
int listLength = nl->ListLength(args);
|
|
if(listLength!=1){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
if( !nl->IsEqual(nl->First(args),CcString::BasicType())
|
|
&& !nl->IsEqual(nl->First(args),FText::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
return nl->SymbolAtom(FText::BasicType());
|
|
}
|
|
|
|
/*
|
|
15.2 Value Mapping for ~readFile~
|
|
|
|
*/
|
|
|
|
template<class T>
|
|
int readFileVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
FText* res = static_cast<FText*>(result.addr);
|
|
T* fileName = static_cast<T*>(args[0].addr);
|
|
if(!fileName->IsDefined()){
|
|
res->Set(false,"");
|
|
return 0;
|
|
}
|
|
string fileNameS = fileName->GetValue();
|
|
if( fileNameS == ""
|
|
|| !FileSystem::FileOrFolderExists(fileNameS)
|
|
|| FileSystem::IsDirectory(fileNameS)
|
|
){
|
|
cerr << "readFile: Cannot open file '" << fileNameS << "'!" << endl;
|
|
res->Set(false,"");
|
|
return 0;
|
|
}
|
|
string Content = "";
|
|
ifstream file;
|
|
file.open(fileNameS.c_str(),ios::binary);
|
|
if(!file.good()){
|
|
cerr << "readFile: Error opening file '" << fileNameS << "'!" << endl;
|
|
res->Set(false,"");
|
|
return 0;
|
|
}
|
|
// read file character by character and check for NULL
|
|
char currChar;
|
|
bool ok = true;
|
|
file.seekg(0,ios::end);
|
|
streampos fileend = file.tellg();
|
|
file.seekg(0,ios::beg);
|
|
while( ok && (file.tellg() != fileend)){
|
|
file.read(reinterpret_cast<char*>(&currChar),1);
|
|
ok = (currChar != '\0');
|
|
if(ok){ // append char to string
|
|
Content.append(1,currChar);
|
|
} else{
|
|
cerr << "readFile: Encountered NULL at position " << file.tellg()
|
|
<< "." << endl;
|
|
}
|
|
}
|
|
file.close();
|
|
if(ok){
|
|
res->Set(true,Content);
|
|
} else {
|
|
res->Set(false,"");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping readFilevaluemap[] = {readFileVM<CcString>,
|
|
readFileVM<FText>};
|
|
|
|
/*
|
|
15.3 Specification for ~readFile~
|
|
|
|
*/
|
|
const string readFileSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {text|string} -> text </text--->"
|
|
"<text> readFile( Name ) </text--->"
|
|
"<text> Reads text from file Name. If the file does not exist, \n"
|
|
"contains a NULL character or some error occurs, the result is "
|
|
"UNDEFINED.</text--->"
|
|
"<text> query readFile('data.csv') </text--->"
|
|
") )";
|
|
|
|
/*
|
|
15.4 Selection Function for ~writeFile~
|
|
|
|
Uses ~stringORtextSelect~.
|
|
|
|
15.5 Operator Instance for operator ~readFile~
|
|
|
|
*/
|
|
Operator readFile ( "readFile",
|
|
readFileSpec,
|
|
2,
|
|
readFilevaluemap,
|
|
stringORtextSelect,
|
|
stringORtext2textTM);
|
|
|
|
|
|
/*
|
|
16 Operator ~moveFile~
|
|
|
|
Move a file to another location within the file system. Returns TRUE,
|
|
iff this succeeds.
|
|
|
|
16.1 TypeMapping for ~moveFile~ and copyFile
|
|
|
|
---- {text|string} x {text|string} -> bool
|
|
----
|
|
|
|
*/
|
|
|
|
ListExpr moveOrCopyFileTM(ListExpr args){
|
|
string err = "{text,string} x {string,text} [x bool] expected";
|
|
int listLength = nl->ListLength(args);
|
|
if(listLength!=2 && listLength!=3){
|
|
ErrorReporter::ReportError(err + " (wrong number of args)");
|
|
return nl->TypeError();
|
|
}
|
|
if(listLength==3 && !CcBool::checkType(nl->Third(args))){
|
|
return listutils::typeError(err + " ( third arg is not a bool)");
|
|
}
|
|
if( !CcString::checkType(nl->First(args))
|
|
&& !FText::checkType(nl->First(args))){
|
|
return listutils::typeError(err + " (first arg is not a strinbg or text)");
|
|
}
|
|
if( !CcString::checkType(nl->Second(args))
|
|
&& !FText::checkType(nl->Second(args))){
|
|
return listutils::typeError(err + " (2nd arg is not a strinbg or text)");
|
|
}
|
|
if(listLength==3){
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
return nl->ThreeElemList( nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList(nl->BoolAtom(false)),
|
|
listutils::basicSymbol<CcBool>());
|
|
}
|
|
|
|
/*
|
|
16.2 Value Mapping for ~moveFile~
|
|
|
|
*/
|
|
|
|
template<class T, class S, bool isMOVE>
|
|
int moveOrCopyFileVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
T* fileNameOld = static_cast<T*>(args[0].addr);
|
|
S* fileNameNew = static_cast<S*>(args[1].addr);
|
|
CcBool* createDir = static_cast<CcBool*>(args[2].addr);
|
|
bool create = createDir->IsDefined()?createDir->GetValue():false;
|
|
if(!fileNameOld->IsDefined() || !fileNameNew->IsDefined()){
|
|
res->Set(false,false);
|
|
return 0;
|
|
}
|
|
string fileNameOldS = fileNameOld->GetValue();
|
|
string fileNameNewS = fileNameNew->GetValue();
|
|
if( fileNameOldS == "" || fileNameNewS == ""
|
|
|| !FileSystem::FileOrFolderExists(fileNameOldS)
|
|
){
|
|
cerr << "moveFile: Cannot open file '" << fileNameOldS << "'!" << endl;
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
if(create){
|
|
string parent = FileSystem::GetParentFolder(fileNameNewS);
|
|
if(parent!=""){
|
|
if(!FileSystem::FileOrFolderExists(parent)){
|
|
FileSystem::CreateFolderEx(parent);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool boolresult = isMOVE
|
|
? FileSystem::RenameFileOrFolder(fileNameOldS,
|
|
fileNameNewS)
|
|
: FileSystem::Copy_File(fileNameOldS, fileNameNewS);
|
|
res->Set(true,boolresult);
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping moveFilevaluemap[] = {moveOrCopyFileVM<CcString, CcString, true>,
|
|
moveOrCopyFileVM<CcString, FText, true>,
|
|
moveOrCopyFileVM<FText, CcString, true>,
|
|
moveOrCopyFileVM<FText, FText, true>};
|
|
|
|
/*
|
|
16.3 Specification for ~moveFile~
|
|
|
|
*/
|
|
const string moveFileSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {text|string} x {text, string} [ x bool ] -> text </text--->"
|
|
"<text> moveFile( OldName, NewName [, createTargetDir] ) </text--->"
|
|
"<text> Move file OldName to file NewName. Can also be used to rename a "
|
|
"file.\nReturns TRUE, iff move-command succeeds.\n If the optional boolean"
|
|
" argument is present and TRUE, the parent directory of the target is "
|
|
"created automatically.\n"
|
|
"WARNING: Be extremely carefully about moving files!\n"
|
|
" The operator also overwrites existing files!</text--->"
|
|
"<text> query moveFile('data.csv', 'data.csv.old') </text--->"
|
|
") )";
|
|
|
|
/*
|
|
16.4 Selection Function for ~moveFile~
|
|
|
|
Uses ~stringORtext\_stringORtextSelect~.
|
|
|
|
|
|
16.5 Operator Instance for operator ~moveFile~
|
|
|
|
*/
|
|
Operator moveFile ( "moveFile",
|
|
moveFileSpec,
|
|
4,
|
|
moveFilevaluemap,
|
|
stringORtext_stringORtext_Select,
|
|
moveOrCopyFileTM);
|
|
|
|
|
|
OperatorSpec copyFileSpec(
|
|
"{string,text} x {string,text} [ x bool] -> bool ",
|
|
"copyFile(source, dest [, createParent] )",
|
|
"Copyies the file source to a file named dest. "
|
|
"Returns the success of this operation.",
|
|
"query copyFile('data.csv','data1.csv')"
|
|
);
|
|
|
|
ValueMapping copyFileVM[] = {moveOrCopyFileVM<CcString, CcString, false>,
|
|
moveOrCopyFileVM<CcString, FText, false>,
|
|
moveOrCopyFileVM<FText, CcString, false>,
|
|
moveOrCopyFileVM<FText, FText, false>};
|
|
|
|
Operator copyFile( "copyFile",
|
|
copyFileSpec.getStr(),
|
|
4,
|
|
copyFileVM,
|
|
stringORtext_stringORtext_Select,
|
|
moveOrCopyFileTM);
|
|
|
|
/*
|
|
17 Operator ~basename~
|
|
|
|
*/
|
|
ListExpr basenameTM(ListExpr args){
|
|
string err ="string or text expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err + "(wrong number of args)");
|
|
}
|
|
ListExpr arg = nl->First(args);
|
|
if(!FText::checkType(arg) && ! CcString::checkType(arg)){
|
|
return listutils::typeError(err);
|
|
}
|
|
return arg;
|
|
}
|
|
|
|
template<class T>
|
|
int basenameVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
T* res = (T*) result.addr;
|
|
T* arg = (T*) args[0].addr;
|
|
if(!arg->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
res->Set(true, FileSystem::Basename(arg->GetValue()));
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping basenameVM[] = {
|
|
basenameVMT<CcString>,
|
|
basenameVMT<FText>
|
|
};
|
|
|
|
int basenameSelect(ListExpr args){
|
|
return CcString::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
OperatorSpec basenameSpec(
|
|
"n -> n, n in {string,text}",
|
|
"basename(_)",
|
|
"Extract the filename without leading path from a pathname",
|
|
"query basename('/home/secondo/secondo.txt')"
|
|
);
|
|
|
|
Operator basenameOp(
|
|
"basename",
|
|
basenameSpec.getStr(),
|
|
2,
|
|
basenameVM,
|
|
basenameSelect,
|
|
basenameTM
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
17 Operator ~getDirectory~
|
|
|
|
Get a stream of text with the listing of the given directory.
|
|
|
|
17.1 TypeMapping for ~getDirectory~
|
|
|
|
---- {text|string} -> stream(text)
|
|
----
|
|
|
|
*/
|
|
|
|
ListExpr stringORtext_OPTIONALint2textstreamTM(ListExpr args){
|
|
string err = "{text|string} [x int] expected";
|
|
int listLength = nl->ListLength(args);
|
|
if(listLength!=1 && listLength!=2){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
if( !nl->IsEqual(nl->First(args),CcString::BasicType())
|
|
&& !nl->IsEqual(nl->First(args),FText::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
if( listLength == 2
|
|
&& !nl->IsEqual(nl->Second(args),CcInt::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
return nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->SymbolAtom(FText::BasicType()));
|
|
}
|
|
|
|
/*
|
|
17.2 Value Mapping for ~getDirectory~
|
|
|
|
*/
|
|
|
|
class GetDirectoryLocalInfo
|
|
{ public:
|
|
GetDirectoryLocalInfo(){
|
|
elements = FilenameList(0);
|
|
iter = elements.begin();
|
|
}
|
|
|
|
~GetDirectoryLocalInfo(){}
|
|
|
|
void SetElements(FilenameList L){
|
|
elements = L;
|
|
iter = elements.begin();
|
|
}
|
|
|
|
FilenameList elements;
|
|
FilenameList::iterator iter;
|
|
};
|
|
|
|
template<class T>
|
|
int getDirectoryVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
GetDirectoryLocalInfo* li;
|
|
result = qp->ResultStorage(s);
|
|
T* dirName = static_cast<T*>(args[0].addr);
|
|
FText* resText;
|
|
int levels = 1;
|
|
|
|
switch(message){
|
|
case OPEN:{
|
|
li = new GetDirectoryLocalInfo();
|
|
local.setAddr(li);
|
|
if(!dirName->IsDefined()){
|
|
cerr << "getDirectory: Directory parameter undefined!"
|
|
<< endl;
|
|
return 0;
|
|
}
|
|
string dirNameS = dirName->GetValue();
|
|
if( dirNameS == ""
|
|
|| !FileSystem::FileOrFolderExists(dirNameS)
|
|
|| !FileSystem::IsDirectory(dirNameS)
|
|
){
|
|
cerr << "getDirectory: Cannot open directory '" << dirNameS << "'!"
|
|
<< endl;
|
|
return 0;
|
|
}
|
|
if(qp->GetNoSons(s)==2){ // get optional 2nd argument
|
|
CcInt* levelsCc = static_cast<CcInt*>(args[1].addr);
|
|
if(!levelsCc->IsDefined()){
|
|
cerr << "getDirectory: Optional recursion parameter undefined!"
|
|
<< endl;
|
|
return 0;
|
|
} else {
|
|
levels = levelsCc->GetIntval();
|
|
if(levels<1){
|
|
levels = 1;
|
|
}
|
|
}
|
|
}
|
|
FilenameList L(0);
|
|
if(FileSystem::FileSearch(dirNameS, L, 0, levels, true, true, 0)){
|
|
li->SetElements(L);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST:{
|
|
if(local.addr){
|
|
li = static_cast<GetDirectoryLocalInfo*>(local.addr);
|
|
}else{
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
if(li->iter == li->elements.end()){
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
resText = new FText(true, *(li->iter));
|
|
result.setAddr(resText);
|
|
++(li->iter);
|
|
return YIELD;
|
|
}
|
|
|
|
case CLOSE:{
|
|
result.setAddr(0);
|
|
if(local.addr){
|
|
li = static_cast<GetDirectoryLocalInfo*>(local.addr);
|
|
delete li;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
// this line should never be reached!
|
|
return -1;
|
|
}
|
|
|
|
ValueMapping getDirectoryvaluemap[] = {getDirectoryVM<CcString>,
|
|
getDirectoryVM<FText>};
|
|
|
|
/*
|
|
17.3 Specification for ~getDirectory~
|
|
|
|
*/
|
|
const string getDirectorySpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {text|string} [ x int] -> stream(text) </text--->"
|
|
"<text> getDirectory( DirectoryName [, N]) </text--->"
|
|
"<text> Create a stream of text containing all file names from directory\n"
|
|
"DirectoryName and its subfolders. The function recurses down to the Nth\n"
|
|
"level. If N is not specified, or N is set to 1 or smaller, meaning that\n"
|
|
"only the direct contents of DirectoryName are listed.</text--->"
|
|
"<text> query getDirectory('tmp', 2)</text--->"
|
|
") )";
|
|
|
|
/*
|
|
17.4 Selection Function for ~getDirectory~
|
|
|
|
Uses ~stringORtextSelect~.
|
|
|
|
|
|
17.5 Operator Instance for operator ~getDirectory~
|
|
|
|
*/
|
|
Operator getDirectory( "getDirectory",
|
|
getDirectorySpec,
|
|
2,
|
|
getDirectoryvaluemap,
|
|
stringORtextSelect,
|
|
stringORtext_OPTIONALint2textstreamTM);
|
|
|
|
/*
|
|
18 Operator ~toCSVtext~
|
|
|
|
Converts any object of a Type from CSVEXPORTABLE to a text object with the
|
|
corresponding CSV text representation.
|
|
|
|
18.1 TypeMapping for ~toCSVtext~
|
|
|
|
---- CSVEXPORTABLE -> text
|
|
----
|
|
|
|
*/
|
|
|
|
ListExpr CSVEXPORTABLEtoTextTM(ListExpr args){
|
|
string err = "CSVEXPORTABLE expected";
|
|
ListExpr errorInfo = nl->OneElemList(nl->SymbolAtom("ERROR"));
|
|
int listLength = nl->ListLength(args);
|
|
if(listLength!=1){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
if(!am->CheckKind(Kind::CSVEXPORTABLE(),nl->First(args),errorInfo)){
|
|
ErrorReporter::ReportError("stream element not in kind csvexportable");
|
|
return nl->TypeError();
|
|
}
|
|
return nl->SymbolAtom(FText::BasicType());
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
18.2 Value Mapping for ~toCSVtext~
|
|
|
|
*/
|
|
|
|
int toCSVtextVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
FText* res = static_cast<FText*>(result.addr);
|
|
Attribute* object = static_cast<Attribute*>(args[0].addr);
|
|
if(!object->IsDefined()){
|
|
res->Set(false,"");
|
|
return 0;
|
|
}
|
|
string objStr = object->getCsvStr();
|
|
res->Set(true,objStr);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
18.3 Specification for ~toCSVtext~
|
|
|
|
*/
|
|
const string toCSVtextSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> X -> text, for X in CSVEXPORTABLE </text--->"
|
|
"<text> toCSVtext( Obj ) </text--->"
|
|
"<text>Converts any object of a CSVEXPORTABLE type to its CSV text\n"
|
|
"representation.</text--->"
|
|
"<text> query toCSVtext(3.14137) </text--->"
|
|
") )";
|
|
|
|
/*
|
|
18.4 Selection Function for ~toCSVtext~
|
|
|
|
Uses ~simpleselect~.
|
|
|
|
|
|
18.5 Operator Instance for operator ~toCSVtext~
|
|
|
|
*/
|
|
Operator toCSVtext( "toCSVtext",
|
|
toCSVtextSpec,
|
|
toCSVtextVM,
|
|
Operator::SimpleSelect,
|
|
CSVEXPORTABLEtoTextTM);
|
|
|
|
|
|
/*
|
|
19 Operator ~fromCSVtext~
|
|
|
|
Converts any object of a Type from CSVEXPORTABLE to a text object with the
|
|
corresponding CSV text representation.
|
|
|
|
19.1 TypeMapping for ~fromCSVtext~
|
|
|
|
---- CSVEXPORTABLE x text -> CSVEXPORTABLE
|
|
----
|
|
|
|
*/
|
|
|
|
ListExpr CSVIMPORTABLE_textORstring2CSVIMPORATABLETM(ListExpr args){
|
|
string err = "CSVIMPORTABLE x {text|string} expected";
|
|
ListExpr errorInfo = nl->OneElemList(nl->SymbolAtom("ERROR"));
|
|
int listLength = nl->ListLength(args);
|
|
if(listLength!=2){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
if(!am->CheckKind(Kind::CSVIMPORTABLE(),nl->First(args),errorInfo)){
|
|
ErrorReporter::ReportError("stream element not in kind csvimportable");
|
|
return nl->TypeError();
|
|
}
|
|
if( !nl->IsEqual(nl->Second(args),CcString::BasicType())
|
|
&& !nl->IsEqual(nl->Second(args),FText::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
return nl->First(args);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
19.2 Value Mapping for ~fromCSVtext~
|
|
|
|
*/
|
|
|
|
template<class T>
|
|
int fromCSVtextVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
T* InText= static_cast<T*>(args[1].addr);
|
|
result = qp->ResultStorage( s );
|
|
Attribute* Res = static_cast<Attribute*>(result.addr);
|
|
|
|
if(!InText->IsDefined()){
|
|
Res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
string myText = InText->GetValue();
|
|
Res->ReadFromString(myText);
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping fromCSVtextvaluemap[] = {fromCSVtextVM<FText>,
|
|
fromCSVtextVM<CcString>};
|
|
|
|
/*
|
|
19.3 Specification for ~fromCSVtext~
|
|
|
|
*/
|
|
const string fromCSVtextSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> X x {text|string} -> X, for X in CSVIMPORTABLE </text--->"
|
|
"<text> fromCSVtext( Pattern, Text ) </text--->"
|
|
"<text>Converts the textual CSV representation of the CSVIMPORTABLE type\n"
|
|
"specified by the type of Pattern to a secondo object.</text--->"
|
|
"<text> query fromCSVtext(0.0, '3.141') </text--->"
|
|
") )";
|
|
|
|
/*
|
|
19.4 Selection Function for ~fromCSVtext~
|
|
|
|
*/
|
|
|
|
int fromCSVtextSelect(ListExpr args){
|
|
if(nl->IsEqual(nl->Second(args),FText::BasicType())){
|
|
return 0;
|
|
} else if(nl->IsEqual(nl->Second(args),CcString::BasicType())){
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
19.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator fromCSVtext( "fromCSVtext",
|
|
fromCSVtextSpec,
|
|
2,
|
|
fromCSVtextvaluemap,
|
|
fromCSVtextSelect,
|
|
CSVIMPORTABLE_textORstring2CSVIMPORATABLETM);
|
|
|
|
/*
|
|
20 Operator ~getPID~
|
|
|
|
20.1 Type Mapping for ~getPID~
|
|
|
|
---- getPID: --> int
|
|
----
|
|
|
|
*/
|
|
ListExpr getPIDTM(ListExpr args){
|
|
if( nl->ListLength(args)==0 ){
|
|
return nl->SymbolAtom(CcInt::BasicType());
|
|
}
|
|
ErrorReporter::ReportError("No argument expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
/*
|
|
20.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int getPIDVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = static_cast<CcInt*>(result.addr);
|
|
res->Set(true, WinUnix::getpid());
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
20.3 Specification
|
|
|
|
*/
|
|
|
|
const string getPIDSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> -> int</text--->"
|
|
"<text>getPID( ) </text--->"
|
|
"<text>Returns the process identifier of the running Secondo kernel "
|
|
"process. The result is always defined.</text--->"
|
|
"<text>query getPID()</text--->"
|
|
") )";
|
|
|
|
/*
|
|
20.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator getPID( "getPID",
|
|
getPIDSpec,
|
|
getPIDVM,
|
|
Operator::SimpleSelect,
|
|
getPIDTM);
|
|
|
|
/*
|
|
21 Operator ~getSecondoVersion~
|
|
|
|
21.1 Type Mapping for ~getSecondoVersion~
|
|
|
|
---- getSecondoVersion: --> string
|
|
----
|
|
|
|
*/
|
|
ListExpr getSecondoVersionTM(ListExpr args){
|
|
if( nl->ListLength(args)==0 ){
|
|
return nl->SymbolAtom(CcString::BasicType());
|
|
}
|
|
ErrorReporter::ReportError("No argument expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
/*
|
|
21.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int getSecondoVersionVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcString* res = static_cast<CcString*>(result.addr);
|
|
ostringstream os;
|
|
os.precision(47);
|
|
os << SECONDO_VERSION_MAJOR << "."
|
|
<< SECONDO_VERSION_MINOR << "."
|
|
<< SECONDO_VERSION_REVISION;
|
|
res->Set( true, os.str() );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
21.3 Specification
|
|
|
|
*/
|
|
|
|
const string getSecondoVersionSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> -> int</text--->"
|
|
"<text>getSecondoVersion( ) </text--->"
|
|
"<text>Returns the version string (version.subversion.revision) for the "
|
|
"running Secondo kernel process. The result is always defined.</text--->"
|
|
"<text>query getSecondoVersion()</text--->"
|
|
") )";
|
|
|
|
/*
|
|
21.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator getSecondoVersion( "getSecondoVersion",
|
|
getSecondoVersionSpec,
|
|
getSecondoVersionVM,
|
|
Operator::SimpleSelect,
|
|
getSecondoVersionTM);
|
|
|
|
|
|
/*
|
|
22 Operator ~getBDBVersion~
|
|
|
|
21.1 Type Mapping for ~getBDBVersion~
|
|
|
|
---- getBDBVersion: --> string
|
|
----
|
|
|
|
*/
|
|
ListExpr getBDBVersionTM(ListExpr args){
|
|
if( nl->ListLength(args)==0 ){
|
|
return nl->SymbolAtom(CcString::BasicType());
|
|
}
|
|
ErrorReporter::ReportError("No argument expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
/*
|
|
22.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int getBDBVersionVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcString* res = static_cast<CcString*>(result.addr);
|
|
ostringstream os;
|
|
os.precision(47);
|
|
os << DB_VERSION_MAJOR << "."
|
|
<< DB_VERSION_MINOR;
|
|
res->Set( true, os.str() );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
22.3 Specification
|
|
|
|
*/
|
|
|
|
const string getBDBVersionSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> -> int</text--->"
|
|
"<text>getBDBVersion( ) </text--->"
|
|
"<text>Returns the version string (version.subversion) for the "
|
|
"running Secondo kernel process' BerkeleyDB version. "
|
|
"The result is always defined.</text--->"
|
|
"<text>query getBDBVersion()</text--->"
|
|
") )";
|
|
|
|
/*
|
|
22.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator getBDBVersion( "getBDBVersion",
|
|
getBDBVersionSpec,
|
|
getBDBVersionVM,
|
|
Operator::SimpleSelect,
|
|
getBDBVersionTM);
|
|
|
|
|
|
/*
|
|
23 Operator ~getSecondoPlatform~
|
|
|
|
23.1 Type Mapping for ~getSecondoPlatform~
|
|
|
|
---- getSecondoPlatform: --> string
|
|
----
|
|
|
|
*/
|
|
ListExpr getSecondoPlatformTM(ListExpr args){
|
|
if( nl->ListLength(args)==0 ){
|
|
return nl->SymbolAtom(CcString::BasicType());
|
|
}
|
|
ErrorReporter::ReportError("No argument expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
/*
|
|
23.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int getSecondoPlatformVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcString* res = static_cast<CcString*>(result.addr);
|
|
res->Set(true, WinUnix::getPlatformStr());
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
23.3 Specification
|
|
|
|
*/
|
|
|
|
const string getSecondoPlatformSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> -> string</text--->"
|
|
"<text>getSecondoPlatform( ) </text--->"
|
|
"<text>Returns the platform, this Secondo kernel is running on. "
|
|
"The result is always defined.</text--->"
|
|
"<text>query getSecondoPlatform()</text--->"
|
|
") )";
|
|
|
|
/*
|
|
23.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator getSecondoPlatform( "getSecondoPlatform",
|
|
getSecondoPlatformSpec,
|
|
getSecondoPlatformVM,
|
|
Operator::SimpleSelect,
|
|
getSecondoPlatformTM);
|
|
|
|
/*
|
|
24 Operator ~getPageSize~
|
|
|
|
24.1 Type Mapping for ~getPageSize~
|
|
|
|
---- getPageSize: --> int
|
|
----
|
|
|
|
*/
|
|
ListExpr getPageSizeTM(ListExpr args){
|
|
if( nl->ListLength(args)==0 ){
|
|
return nl->SymbolAtom(CcInt::BasicType());
|
|
}
|
|
ErrorReporter::ReportError("No argument expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
/*
|
|
24.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int getPageSizeVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = static_cast<CcInt*>(result.addr);
|
|
res->Set(true, WinUnix::getPageSize());
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
24.3 Specification
|
|
|
|
*/
|
|
|
|
const string getPageSizeSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> -> int</text--->"
|
|
"<text>getPageSize( ) </text--->"
|
|
"<text>Returns the file-system's page size. "
|
|
"The result is always defined.</text--->"
|
|
"<text>query getPageSize()</text--->"
|
|
") )";
|
|
|
|
/*
|
|
24.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator getPageSize( "getPageSize",
|
|
getPageSizeSpec,
|
|
getPageSizeVM,
|
|
Operator::SimpleSelect,
|
|
getPageSizeTM);
|
|
|
|
|
|
|
|
|
|
/*
|
|
25 Operator SQLExport
|
|
|
|
This operator takes a tupel stream and writes a SQL script into a file.
|
|
|
|
|
|
25.1 TypeMapping
|
|
|
|
|
|
The signature is (stream(tuple)) x string x {text,string} [x bool] -> bool
|
|
|
|
All attributes within the tuple stream must be in KIND SQLexportable
|
|
|
|
*/
|
|
ListExpr sqlExportTM(ListExpr args){
|
|
int len = nl->ListLength(args);
|
|
string err = "stream(tuple) x {text,string} x string [x bool] expected";
|
|
if((len!=3) && (len!=4) ){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr fileName = nl->Second(args);
|
|
ListExpr tabName = nl->Third(args);
|
|
|
|
if(!Stream<Tuple>::checkType(stream)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!CcString::checkType(fileName) && !FText::checkType(fileName)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!CcString::checkType(tabName)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
if(len==4){
|
|
if(!CcBool::checkType(nl->Fourth(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
}
|
|
ListExpr attrList = nl->Second(nl->Second(stream));
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr first = nl->First(attrList);
|
|
attrList = nl->Rest(attrList);
|
|
ListExpr type = nl->Second(first);
|
|
if(!listutils::isKind(type,Kind::SQLEXPORTABLE())){
|
|
string name = nl->SymbolValue(nl->First(first));
|
|
return listutils::typeError("Attribute " + name +
|
|
" is not in kind SQLEXPORTABLE");
|
|
}
|
|
}
|
|
if(len==4){
|
|
return listutils::basicSymbol<CcBool>();
|
|
} else {
|
|
return nl->ThreeElemList( nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList(nl->BoolAtom(false)),
|
|
listutils::basicSymbol<CcBool>());
|
|
}
|
|
}
|
|
|
|
|
|
template<class T>
|
|
int sqlExportVM1(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
|
|
T* FileName = (T*) args[1].addr;
|
|
CcString* TabName = (CcString*) args[2].addr;
|
|
CcBool* Overwrite = (CcBool*) args[3].addr;
|
|
if(!FileName->IsDefined() || ! Overwrite->IsDefined() ||
|
|
!TabName->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
string filename = FileName->GetValue();
|
|
bool overwrite = Overwrite->GetBoolval();
|
|
|
|
if(FileSystem::FileOrFolderExists(filename) && !overwrite){
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
fstream out;
|
|
out.open(filename.c_str(), ios::out);
|
|
if(!out.good()){
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
|
|
Stream<Tuple> stream(args[0]);
|
|
stream.open();
|
|
Tuple* tuple = 0;
|
|
|
|
string tabname = TabName->GetValue();
|
|
|
|
string namelist="";
|
|
|
|
bool first = true;
|
|
while(out.good() && ( (tuple = stream.request() )!=0)){
|
|
if(first){ // write the table create command to out
|
|
out << "CREATE TABLE "
|
|
<< TabName->GetValue() << " ( " << endl;
|
|
|
|
|
|
ListExpr list = qp->GetType(qp->GetSon(s,0));
|
|
ListExpr attrList = nl->Second(nl->Second(list));
|
|
assert(nl->ListLength(attrList) == tuple->GetNoAttributes());
|
|
|
|
for(int i=0;i<tuple->GetNoAttributes();i++){
|
|
string name = nl->SymbolValue(nl->First(nl->First(attrList)));
|
|
if(i>0) {
|
|
namelist += ", ";
|
|
} else {
|
|
namelist += " ";
|
|
}
|
|
namelist += name;
|
|
attrList = nl->Rest(attrList);
|
|
out << name << " " << tuple->GetAttribute(i)->getSQLType()
|
|
<< " ," << endl;
|
|
}
|
|
out << ");" << endl;
|
|
first = false;
|
|
}
|
|
|
|
out << "INSERT INTO " << tabname <<"(" << namelist << ") Values (";
|
|
for(int i=0;i<tuple->GetNoAttributes();i++){
|
|
if(i>0){
|
|
out << ", ";
|
|
} else {
|
|
out << " ";
|
|
}
|
|
out << tuple->GetAttribute(i)->getSQLRepresentation();
|
|
}
|
|
out << ");" << endl;
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
|
|
out.close();
|
|
stream.close();
|
|
res->Set(true,tuple==0);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Value Mapping Array
|
|
|
|
*/
|
|
ValueMapping sqlExportVM[] = {
|
|
sqlExportVM1<CcString>,
|
|
sqlExportVM1<FText>
|
|
};
|
|
|
|
|
|
/*
|
|
Selection function
|
|
|
|
*/
|
|
int sqlExportSelect(ListExpr args){
|
|
return CcString::checkType(nl->Second(args))?0:1;
|
|
}
|
|
|
|
/*
|
|
Specification
|
|
|
|
*/
|
|
OperatorSpec sqlExportSpec(
|
|
"stream(tuple) x {text,string} x string [ x bool] ", // signature
|
|
" <stream> sqlExport[ fileName, tabName [, overwriteExisting] ] " , //syntax
|
|
" writes an sql script fot importing a stream" ,
|
|
" query ten feed sqlExport[\"ten.sql\",\"ten\",TRUE]"
|
|
);
|
|
|
|
/*
|
|
Operator Instance
|
|
|
|
*/
|
|
Operator sqlExport(
|
|
"sqlExport",
|
|
sqlExportSpec.getStr(),
|
|
2,
|
|
sqlExportVM,
|
|
sqlExportSelect,
|
|
sqlExportTM
|
|
);
|
|
|
|
|
|
/*
|
|
24.25 Operator importHGT1
|
|
|
|
24.25.1 TypeMapping
|
|
|
|
Signature is text -> stream(tuple((R : rect, V : int))
|
|
|
|
*/
|
|
ListExpr importHGT1TM(ListExpr args){
|
|
string err = "text expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(FText::checkType(nl->First(args))){
|
|
return nl->TwoElemList(listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(listutils::basicSymbol<Tuple>(),
|
|
nl->TwoElemList(
|
|
nl->TwoElemList(nl->SymbolAtom("R"),
|
|
listutils::basicSymbol<Rectangle<2> >()),
|
|
nl->TwoElemList(nl->SymbolAtom("V"),
|
|
listutils::basicSymbol<CcInt>()))));
|
|
}
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
/*
|
|
24.25.2 Value Mapping
|
|
|
|
*/
|
|
|
|
class ImportHGT1Local{
|
|
|
|
public:
|
|
ImportHGT1Local(FText* name, ListExpr t){
|
|
little = WinUnix::isLittleEndian();
|
|
undefvalue = -32768; // given by file format
|
|
tt = new TupleType(t);
|
|
f = 0;
|
|
if(!name->IsDefined()){
|
|
return;
|
|
}
|
|
string names = name->GetValue();
|
|
if(names.length() < 4){
|
|
return;
|
|
}
|
|
stringstream ss(names);
|
|
char c;
|
|
ss.get(c);
|
|
int signum1 = tolower(c)=='s'?-1:1;
|
|
|
|
int y;
|
|
ss >> y;
|
|
ss.get(c);
|
|
int signum2 = tolower(c)=='w'?-1:1;
|
|
int x;
|
|
ss >> x;
|
|
if( (y>90) || (x > 180)){
|
|
return;
|
|
}
|
|
x = x * signum2;
|
|
y = y * signum1;
|
|
lbx = x;
|
|
lby = y+1;
|
|
f = new ifstream(names.c_str(), ios::in|ios::binary|ios::ate);
|
|
if(!f->is_open()){
|
|
delete f;
|
|
f = 0;
|
|
return;
|
|
}
|
|
ifstream::pos_type size = f->tellg();
|
|
if(size==25934402) { // == 3601 * 3601 * 2
|
|
gridsize = 3601;
|
|
} else if(size==2884802) { // 1201 * 1201 * 2
|
|
gridsize = 1201;
|
|
} else {
|
|
f->close();
|
|
delete f;
|
|
f = 0;
|
|
}
|
|
posx = 0;
|
|
posy = 0;
|
|
cellsize = 1.0 / (double) gridsize;
|
|
f->seekg (0, ios::beg);
|
|
}
|
|
|
|
|
|
~ImportHGT1Local(){
|
|
if(f){
|
|
f->close();
|
|
delete f;
|
|
f = 0;
|
|
}
|
|
tt->DeleteIfAllowed();
|
|
}
|
|
|
|
Tuple* next(){
|
|
if(!f){
|
|
// error during initialization
|
|
return 0;
|
|
}
|
|
if( posy==gridsize){
|
|
return 0;
|
|
}
|
|
if(!f->good()){
|
|
cout << " file not good" << endl;
|
|
return 0;
|
|
}
|
|
|
|
if(posx==0){ // read next line from file
|
|
f->read((char*) buffer, gridsize*2);
|
|
if(!f->good()){
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int16_t v = buffer[posx];
|
|
|
|
if(little){
|
|
//cout << "convert " << v ;
|
|
v = WinUnix::convertEndian((uint16_t)v);
|
|
//cout << " into " << v << endl;
|
|
}
|
|
|
|
double min[2];
|
|
double max[2];
|
|
min[0] = lbx + cellsize*posx;
|
|
min[1] = lby - cellsize*(posy+1);
|
|
max[0] = lbx + cellsize*(posx+1);
|
|
max[1] = lby - cellsize*(posy);
|
|
|
|
Tuple* res = new Tuple(tt);
|
|
res->PutAttribute(0, new Rectangle<2>(true,min,max));
|
|
if(v!=undefvalue){
|
|
res->PutAttribute(1, new CcInt(true,v));
|
|
} else {
|
|
res->PutAttribute(1, new CcInt(false));
|
|
}
|
|
posx++;
|
|
if(posx==gridsize){
|
|
posy++;
|
|
posx=0;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
ifstream* f;
|
|
int gridsize;
|
|
double cellsize;
|
|
int lbx;
|
|
int lby;
|
|
int posx;
|
|
int posy;
|
|
TupleType* tt;
|
|
bool little;
|
|
int16_t buffer[3601]; // buffer for reading from file
|
|
int undefvalue;
|
|
|
|
};
|
|
|
|
int importHGT1VM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
ImportHGT1Local* li = (ImportHGT1Local*) local.addr;
|
|
switch(message){
|
|
case OPEN : { if(li){
|
|
delete li;
|
|
}
|
|
local.addr = new ImportHGT1Local( (FText*) args[0].addr,
|
|
nl->Second(GetTupleResultType(s)));
|
|
return 0;
|
|
}
|
|
case REQUEST : { result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE : {
|
|
if(li){ delete li; }
|
|
local.addr = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
Specification
|
|
|
|
*/
|
|
OperatorSpec importHGT1Spec(
|
|
" text -> stream(tuple([R : rect, V : int)) ", // signature
|
|
" importGHT1(_)" , //syntax
|
|
" imports an HGT file" ,
|
|
" query importGHT1('N55W51.hgt') consume"
|
|
);
|
|
|
|
/*
|
|
Operator instance
|
|
|
|
*/
|
|
Operator importGHT1(
|
|
"importHGT1",
|
|
importHGT1Spec.getStr(),
|
|
importHGT1VM,
|
|
Operator::SimpleSelect,
|
|
importHGT1TM);
|
|
|
|
|
|
|
|
/*
|
|
24.26 Operator ~removeDirectory~
|
|
|
|
*/
|
|
|
|
ListExpr removeDirectoryTM(ListExpr args){
|
|
|
|
string err = "{string,text} [ x bool] expected";
|
|
if(!nl->HasLength(args,1) && !nl->HasLength(args,2)){
|
|
return listutils::typeError(err + " (wrong number of args)");
|
|
}
|
|
ListExpr f = nl->First(args);
|
|
if(!CcString::checkType(f) && !FText::checkType(f)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
if(nl->HasLength(args,1)){
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList(nl->BoolAtom(false)),
|
|
listutils::basicSymbol<CcBool>());
|
|
}
|
|
|
|
if(!CcBool::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
|
|
template<class T>
|
|
int removeDirectoryVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
T* file = (T*) args[0].addr;
|
|
CcBool* rek = (CcBool*) args[1].addr;
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
|
|
if(!file->IsDefined() || !rek->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
string fn = file->GetValue();
|
|
bool r = rek->GetValue();
|
|
|
|
if(!FileSystem::IsDirectory(fn)){
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
|
|
if(r){
|
|
res->Set(true,FileSystem::EraseFolder(fn));
|
|
} else {
|
|
res->Set(true,FileSystem::DeleteFileOrFolder(fn));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int removeDirectorySelect(ListExpr args){
|
|
return CcString::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
ValueMapping removeDirectoryVM[] = {
|
|
removeDirectoryVMT<CcString>,
|
|
removeDirectoryVMT<FText>
|
|
};
|
|
|
|
OperatorSpec removeDirectorySpec(
|
|
"{string,text} [x bool] -> bool",
|
|
"removeDirectory( filename, recursive)",
|
|
"Removes a directory on the filesystem. "
|
|
"If the boolean argument is true, all subfolders "
|
|
"of that directory will be removed too. Otherwise "
|
|
"only empty directories will be deleted. The default "
|
|
"value for the flag is false.",
|
|
"query removeDirectory('Blub.dir', TRUE)"
|
|
);
|
|
|
|
Operator removeDirectoryOp(
|
|
"removeDirectory",
|
|
removeDirectorySpec.getStr(),
|
|
2,
|
|
removeDirectoryVM,
|
|
removeDirectorySelect,
|
|
removeDirectoryTM
|
|
);
|
|
|
|
|
|
|
|
ListExpr shpBoxTM(ListExpr args){
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError("One argument expected");
|
|
}
|
|
ListExpr arg = nl->First(args);
|
|
if(!CcString::checkType(arg) && !FText::checkType(arg)){
|
|
return listutils::typeError("string or text expected");
|
|
}
|
|
return listutils::basicSymbol<Rectangle<2> >();
|
|
}
|
|
|
|
|
|
template<class T>
|
|
int shpBoxVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
T* f = (T*) args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
Rectangle<2>* res = (Rectangle<2>*) result.addr;
|
|
if(!f->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
ifstream in(f->GetValue().c_str(), ios::in|ios::binary);
|
|
if(!in.good()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
in.seekg(0,ios::beg);
|
|
uint32_t code;
|
|
uint32_t version;
|
|
uint32_t type;
|
|
in.read(reinterpret_cast<char*>(&code),4);
|
|
in.seekg(28,ios::beg);
|
|
in.read(reinterpret_cast<char*>(&version),4);
|
|
in.read(reinterpret_cast<char*>(&type),4);
|
|
if(WinUnix::isLittleEndian()){
|
|
code = WinUnix::convertEndian(code);
|
|
} else {
|
|
version = WinUnix::convertEndian(version);
|
|
type = WinUnix::convertEndian(type);
|
|
}
|
|
if(code!=9994){ // not a shapefile
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
uint64_t xmin;
|
|
uint64_t ymin;
|
|
uint64_t xmax;
|
|
uint64_t ymax;
|
|
in.read(reinterpret_cast<char*>(&xmin),8);
|
|
in.read(reinterpret_cast<char*>(&ymin),8);
|
|
in.read(reinterpret_cast<char*>(&xmax),8);
|
|
in.read(reinterpret_cast<char*>(&ymax),8);
|
|
if(!in.good()){ // corrupt file
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
in.close();
|
|
if(!WinUnix::isLittleEndian()){
|
|
xmin = WinUnix::convertEndian(xmin);
|
|
ymin = WinUnix::convertEndian(ymin);
|
|
xmax = WinUnix::convertEndian(xmax);
|
|
ymax = WinUnix::convertEndian(ymax);
|
|
}
|
|
void* d=&xmin;
|
|
double xmind = *(reinterpret_cast<double*>(d));
|
|
d = &ymin;
|
|
double ymind = *(reinterpret_cast<double*>(d));
|
|
d = &xmax;
|
|
double xmaxd = *(reinterpret_cast<double*>(d));
|
|
d = &ymax;
|
|
double ymaxd = *(reinterpret_cast<double*>(d));
|
|
double minD[] = {xmind,ymind};
|
|
double maxD[] = {xmaxd, ymaxd};
|
|
res->Set(true, minD, maxD );
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping shpBoxVM[] = {
|
|
shpBoxVMT<CcString>,
|
|
shpBoxVMT<FText>
|
|
} ;
|
|
|
|
int shpBoxSelect(ListExpr args){
|
|
return CcString::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
OperatorSpec shpBoxSpec(
|
|
" string|text -> rect",
|
|
" shpBox(_)",
|
|
"Extracts the bounding box information from a shapefile.",
|
|
"query shpBox('kinos.shp')"
|
|
);
|
|
|
|
Operator shpBoxOp(
|
|
"shpBox",
|
|
shpBoxSpec.getStr(),
|
|
2,
|
|
shpBoxVM,
|
|
shpBoxSelect,
|
|
shpBoxTM
|
|
);
|
|
|
|
|
|
/*
|
|
24.26 collectShpFiles
|
|
|
|
This operator connects a set of shape file into a single one.
|
|
The arguments are a stream of names and the file name for
|
|
the result file.
|
|
|
|
The output is a tuple stream containing the original file names,
|
|
a boolean value reporting the success for this file and a text
|
|
representing an error message if the was no success.
|
|
|
|
*/
|
|
|
|
ListExpr shpCollectTM(ListExpr args){
|
|
if(!nl->HasLength(args,3)){
|
|
return listutils::typeError("invalid number of arguments");
|
|
}
|
|
if(!Stream<CcString>::checkType(nl->First(args))
|
|
&& !Stream<FText>::checkType(nl->First(args))){
|
|
return listutils::typeError("the first argument has to be a stream "
|
|
"of string or a stream of text");
|
|
}
|
|
if(!CcBool::checkType(nl->Second(args))){
|
|
return listutils::typeError("second argument has to be of type bool");
|
|
}
|
|
|
|
if(!CcString::checkType(nl->Third(args))
|
|
&&!FText::checkType(nl->Third(args))){
|
|
return listutils::typeError("the third arg ist neihter "
|
|
"a string nor a text");
|
|
}
|
|
return nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(
|
|
listutils::basicSymbol<Tuple>(),
|
|
nl->ThreeElemList(
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("File"),
|
|
nl->Second(nl->First(args))),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("Success"),
|
|
listutils::basicSymbol<CcBool>()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("ErrMsg"),
|
|
listutils::basicSymbol<FText>()))));
|
|
}
|
|
|
|
|
|
|
|
template<class T>
|
|
class shpCollectInfo{
|
|
public:
|
|
shpCollectInfo(Word& _stream,
|
|
const string& fn,
|
|
bool createShx,
|
|
ListExpr _tt):
|
|
stream(_stream){
|
|
outshp = new ofstream(fn.c_str(), ios::binary| ios::out );
|
|
char* p = WinUnix::getAbsolutePath(fn.c_str());
|
|
outabsolutepath = string(p);
|
|
free(p);
|
|
outbuf = new char[FILE_BUFFER_SIZE];
|
|
outshp->rdbuf()->pubsetbuf(outbuf,FILE_BUFFER_SIZE);
|
|
|
|
if(!createShx){
|
|
outshx = 0;
|
|
shxbuf = 0;
|
|
} else {
|
|
string name = fn;
|
|
stringutils::toLower(name);
|
|
if(stringutils::endsWith(name,".shp")){
|
|
name = fn.substr(0,fn.length()-4);
|
|
} else {
|
|
name = fn;
|
|
}
|
|
name += ".shx";
|
|
outshx = new ofstream(name.c_str(), ios::binary | ios::out);
|
|
shxbuf = new char[FILE_BUFFER_SIZE];
|
|
outshx->rdbuf()->pubsetbuf(shxbuf,FILE_BUFFER_SIZE);
|
|
}
|
|
|
|
|
|
stream.open();
|
|
tt = new TupleType(_tt);
|
|
shpType = -1;
|
|
noRecords = 0;
|
|
}
|
|
|
|
~shpCollectInfo(){
|
|
// write new bounding box into output file
|
|
uint64_t s = outshp->tellp();
|
|
uint32_t s2 = s/2;
|
|
if(s>100){ // header length of a shape file
|
|
outshp->seekp(24);
|
|
WinUnix::writeBigEndian(*outshp,s2); // store file size
|
|
outshp->seekp(36); // position of XMin
|
|
WinUnix::writeLittle64(*outshp,minX);
|
|
WinUnix::writeLittle64(*outshp,minY);
|
|
WinUnix::writeLittle64(*outshp,maxX);
|
|
WinUnix::writeLittle64(*outshp,maxY);
|
|
}
|
|
outshp->close();
|
|
delete[] outbuf;
|
|
delete outshp;
|
|
|
|
if(outshx){ // write the same information into shx file if required
|
|
s = outshx->tellp();
|
|
s2 = s/2;
|
|
if(s>100){
|
|
outshx->seekp(24);
|
|
WinUnix::writeBigEndian(*outshx,s2); // store file size
|
|
outshx->seekp(36); // position of XMin
|
|
WinUnix::writeLittle64(*outshx,minX);
|
|
WinUnix::writeLittle64(*outshx,minY);
|
|
WinUnix::writeLittle64(*outshx,maxX);
|
|
WinUnix::writeLittle64(*outshx,maxY);
|
|
}
|
|
outshx->close();
|
|
delete[] shxbuf;
|
|
delete outshx;
|
|
}
|
|
|
|
stream.close();
|
|
tt->DeleteIfAllowed();
|
|
}
|
|
|
|
Tuple* next(){
|
|
T* f = stream.request();
|
|
if(!f){
|
|
return 0;
|
|
}
|
|
Tuple* res = new Tuple(tt);
|
|
res->PutAttribute(0,f);
|
|
if(!f->IsDefined()){
|
|
return error(res,"undefined file name",0);
|
|
}
|
|
appendFile(res, f->GetValue());
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
Stream<T> stream;
|
|
ofstream* outshp;
|
|
ofstream* outshx;
|
|
int32_t shpType;
|
|
double minX;
|
|
double minY;
|
|
double maxX;
|
|
double maxY;
|
|
TupleType* tt;
|
|
uint32_t noRecords;
|
|
char* outbuf;
|
|
char* shxbuf;
|
|
string outabsolutepath;
|
|
|
|
Tuple* error(Tuple* res, string message, ifstream* inshp=0,
|
|
char* inbuf = 0 ){
|
|
res->PutAttribute(1, new CcBool(true,false));
|
|
res->PutAttribute(2, new FText(true, message));
|
|
if(inshp) delete inshp;
|
|
if(inbuf) delete[] inbuf;
|
|
return res;
|
|
}
|
|
|
|
Tuple* appendFile(Tuple* res, const string& fileName){
|
|
char* inpath = WinUnix::getAbsolutePath(fileName.c_str());
|
|
string inp = string(inpath);
|
|
free(inpath);
|
|
if(inp == outabsolutepath){
|
|
return error(res, "file identically to output");
|
|
}
|
|
|
|
ifstream* inshp = new ifstream(fileName.c_str(), ios::binary);
|
|
if(!inshp->good()){
|
|
return error(res, "could not open shape file", inshp);
|
|
}
|
|
char* inbuf = new char[FILE_BUFFER_SIZE];
|
|
inshp->rdbuf()->pubsetbuf(inbuf,FILE_BUFFER_SIZE);
|
|
// the file can be opened, now check the type
|
|
int32_t code = readBigInt32(*inshp);
|
|
if(!inshp->good()){
|
|
return error(res,"problem in shape file", inshp, inbuf);
|
|
}
|
|
if(code !=9994){
|
|
return error(res,"not a shape file", inshp,inbuf);
|
|
}
|
|
// read shape type and bounding box
|
|
inshp->seekg(32);
|
|
int32_t type = readLittleInt32(*inshp);
|
|
double _minX = readLittleDouble(*inshp);
|
|
double _minY = readLittleDouble(*inshp);
|
|
double _maxX = readLittleDouble(*inshp);
|
|
double _maxY = readLittleDouble(*inshp);
|
|
if(!inshp->good()){
|
|
return error(res,"problem in shape file", inshp,inbuf);
|
|
}
|
|
if(type!=1 && type!=3 && type!=5 && type!=8){
|
|
return error(res, "unsupported shape type", inshp,inbuf);
|
|
}
|
|
|
|
if(noRecords==0){ // first file
|
|
shpType = type;
|
|
minX = _minX;
|
|
maxX = _maxX;
|
|
minY = _minY;
|
|
maxY = _maxY;
|
|
// copy header into the new file
|
|
char header[100];
|
|
outshp->seekp(0); // if there was an empty shape file
|
|
inshp->seekg(0);
|
|
inshp->read(header, 100);
|
|
outshp->write(header,100);
|
|
if(outshx){
|
|
outshx->seekp(0);
|
|
outshx->write(header,100);
|
|
}
|
|
} else {
|
|
if(shpType != type){
|
|
return error(res, "shape type differs", inshp,inbuf);
|
|
}
|
|
if(_minX < minX) minX = _minX;
|
|
if(_maxX > maxX) maxX = _maxX;
|
|
if(_minY < minY) minY = _minY;
|
|
if(_maxY > maxY) maxY = _maxY;
|
|
inshp->seekg(100); // overread header
|
|
}
|
|
uint32_t buffersize = 16*1024;
|
|
char buffer[buffersize]; // maximum buffer size for a single value
|
|
bool done = false;
|
|
while(!inshp->eof() && !done && inshp->good()){
|
|
// read record number from in
|
|
readBigInt32(*inshp);
|
|
if(!inshp->eof() && inshp->good()){
|
|
// read contents length
|
|
uint32_t length = readBigInt32(*inshp);
|
|
// write output recno and length to output
|
|
if(inshp->good()){
|
|
noRecords++;
|
|
uint32_t offset = outshp->tellp();
|
|
WinUnix::writeBigEndian(*outshp, noRecords);
|
|
WinUnix::writeBigEndian(*outshp, length);
|
|
uint32_t chars = length*2;
|
|
// copy content
|
|
while(chars>0){
|
|
uint32_t toCopy = (uint32_t)(chars>buffersize?buffersize:chars);
|
|
inshp->read(buffer,toCopy);
|
|
outshp->write(buffer,toCopy);
|
|
chars -= toCopy;
|
|
}
|
|
// write info to shx file
|
|
if(outshx){
|
|
WinUnix::writeBigEndian(*outshx,offset/2);
|
|
WinUnix::writeBigEndian(*outshx,length);
|
|
}
|
|
}
|
|
} else {
|
|
done = true;
|
|
}
|
|
}
|
|
|
|
|
|
inshp->close();
|
|
delete inshp;
|
|
delete[] inbuf;
|
|
res->PutAttribute(1, new CcBool(true,true));
|
|
res->PutAttribute(2, new FText(true,""));
|
|
return res;
|
|
}
|
|
};
|
|
|
|
|
|
template<class T, class F>
|
|
int shpCollectVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
shpCollectInfo<T>* li = (shpCollectInfo<T>*) local.addr;
|
|
switch(message){
|
|
case OPEN : {
|
|
if(li) {
|
|
delete li;
|
|
local.addr=0;
|
|
}
|
|
CcBool* x = (CcBool*) args[1].addr;
|
|
bool createShx = x->IsDefined() && x->GetValue();
|
|
|
|
F* fn = (F*) args[2].addr;
|
|
if(!fn->IsDefined()){
|
|
return 0;
|
|
}
|
|
ListExpr tt = nl->Second(GetTupleResultType(s));
|
|
local.addr = new shpCollectInfo<T>(args[0],
|
|
fn->GetValue(),
|
|
createShx,
|
|
tt);
|
|
return 0;
|
|
}
|
|
case REQUEST : {
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE : {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
ValueMapping shpCollectVM[] = {
|
|
shpCollectVMT<CcString,CcString>,
|
|
shpCollectVMT<CcString,FText>,
|
|
shpCollectVMT<FText,CcString>,
|
|
shpCollectVMT<FText,FText>
|
|
};
|
|
|
|
int shpCollectSelect(ListExpr args){
|
|
int n1 = Stream<CcString>::checkType(nl->First(args))?0:2;
|
|
int n2 = CcString::checkType(nl->Third(args))?0:1;
|
|
return n1+n2;
|
|
}
|
|
|
|
OperatorSpec shpCollectSpec(
|
|
" stream({text,string}) x bool x {text, string} -> stream(tuple) ",
|
|
" _ shpcollect[_,_]",
|
|
"Puts some shape files whose names comes from the stream "
|
|
"into a single shape file. If the boolean argument is true, "
|
|
"the corresponding shx file is created. "
|
|
"The output is a tuple stream "
|
|
"reporting for each file whether the connect was successful "
|
|
"and in case of an error with an error message.",
|
|
" query getdirectory(\".\") filter[ . endsWith \".shp\"] "
|
|
"shpCollect[TRUE, 'all.shp']"
|
|
);
|
|
|
|
Operator shpCollectOp(
|
|
"shpCollect",
|
|
shpCollectSpec.getStr(),
|
|
4,
|
|
shpCollectVM,
|
|
shpCollectSelect,
|
|
shpCollectTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
4.26 Operator ~db3Collect~
|
|
|
|
4.26.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr db3CollectTM(ListExpr args){
|
|
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError("invalid number of arguments");
|
|
}
|
|
if(!Stream<CcString>::checkType(nl->First(args))
|
|
&& !Stream<FText>::checkType(nl->First(args))){
|
|
return listutils::typeError("the first argument has to be a stream "
|
|
"of string or a stream of text");
|
|
}
|
|
if(!CcString::checkType(nl->Second(args))
|
|
&&!FText::checkType(nl->Second(args))){
|
|
return listutils::typeError("the second arg ist neihter "
|
|
"a string nor a text");
|
|
}
|
|
return nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(
|
|
listutils::basicSymbol<Tuple>(),
|
|
nl->ThreeElemList(
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("File"),
|
|
nl->Second(nl->First(args))),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("Success"),
|
|
listutils::basicSymbol<CcBool>()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("ErrMsg"),
|
|
listutils::basicSymbol<FText>()))));
|
|
}
|
|
|
|
/*
|
|
4.26.2 The LocalInfo Class
|
|
|
|
*/
|
|
|
|
class db3AttrType{
|
|
public:
|
|
db3AttrType(char* info): name(info,11u),type(info[11]),
|
|
length(info[16]),
|
|
noDecimals(info[17]){
|
|
name = string(name.c_str());
|
|
}
|
|
|
|
bool operator==(const db3AttrType& rhs){
|
|
return name==rhs.name
|
|
&& type==rhs.type
|
|
&& length==rhs.length
|
|
&& noDecimals==rhs.noDecimals;
|
|
}
|
|
bool operator!=(const db3AttrType& rhs){
|
|
return name!=rhs.name
|
|
|| type!=rhs.type
|
|
|| length!=rhs.length
|
|
|| noDecimals!=rhs.noDecimals;
|
|
}
|
|
|
|
bool isValid(){
|
|
if( type!= 'C' && type!='N' && type!='L'
|
|
&& type != 'D' && type!='M'){
|
|
print(cerr);
|
|
return false;
|
|
}
|
|
bool res = stringutils::isIdent(name);
|
|
if(!res){
|
|
print(cerr); cerr << endl;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
void print(ostream& out){
|
|
out << name << ", T:" << type << ", L:" << length
|
|
<< ", D : " << noDecimals << endl;
|
|
}
|
|
|
|
|
|
private:
|
|
string name;
|
|
unsigned char type;
|
|
unsigned char length;
|
|
unsigned char noDecimals;
|
|
};
|
|
|
|
|
|
template<class T>
|
|
class db3CollectInfo{
|
|
public:
|
|
db3CollectInfo(Word _stream, const string& _fileName,
|
|
ListExpr _tt): stream(_stream){
|
|
tt = new TupleType(_tt);
|
|
char* outpath;
|
|
if( (outpath=WinUnix::getAbsolutePath(_fileName.c_str())) == 0){
|
|
outabsolutepath = "";
|
|
} else {
|
|
outabsolutepath = string(outpath);
|
|
}
|
|
free(outpath);
|
|
out = new ofstream(_fileName.c_str(),ios_base::binary);
|
|
outbuf = new char[FILE_BUFFER_SIZE];
|
|
out->rdbuf()->pubsetbuf(outbuf,FILE_BUFFER_SIZE);
|
|
noRecords = 0;
|
|
stream.open();
|
|
}
|
|
|
|
~db3CollectInfo(){
|
|
if(out->good() && out->tellp()>8){
|
|
// TODO: change modification date (not important)
|
|
out->seekp(4);
|
|
WinUnix::writeLittleEndian(*out,noRecords);
|
|
}
|
|
out->close();
|
|
delete out;
|
|
delete [] outbuf;
|
|
tt->DeleteIfAllowed();
|
|
stream.close();
|
|
}
|
|
|
|
|
|
Tuple* next() {
|
|
T* fn = stream.request();
|
|
if(!fn){
|
|
return 0;
|
|
}
|
|
Tuple* res = new Tuple(tt);
|
|
res->PutAttribute(0,fn);
|
|
if(!fn->IsDefined()){
|
|
return error(res,"Undefined file name");
|
|
}
|
|
return processFile(res, fn->GetValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
Stream<T> stream;
|
|
TupleType* tt;
|
|
ofstream* out;
|
|
char* outbuf;
|
|
uint32_t noRecords;
|
|
vector<db3AttrType> dbType;
|
|
bool hasMemo;
|
|
string outabsolutepath;
|
|
|
|
Tuple* error(Tuple* res, const string& msg,
|
|
ifstream* in1 = 0, ifstream* in2=0,
|
|
char* in1buf=0, char* in2buf=0){
|
|
res->PutAttribute(1, new CcBool(true,false));
|
|
res->PutAttribute(2, new FText(true,msg));
|
|
if(in1){
|
|
in1->close();
|
|
}
|
|
if(in2){
|
|
in2->close();
|
|
}
|
|
if(in1buf) delete[] in1buf;
|
|
if(in2buf) delete[] in2buf;
|
|
return res;
|
|
}
|
|
|
|
|
|
Tuple* processFile(Tuple* res, const string& name){
|
|
char* inpath = WinUnix::getAbsolutePath(name.c_str());
|
|
string inabs = string(inpath);
|
|
free(inpath);
|
|
if(inabs==outabsolutepath){
|
|
return error(res,"input file identically to output");
|
|
}
|
|
|
|
|
|
ifstream in1(name.c_str(), ios_base::binary);
|
|
if(!in1.good()){
|
|
return error(res,"Cannot open file", &in1);
|
|
}
|
|
char* in1buf = new char[FILE_BUFFER_SIZE];
|
|
in1.rdbuf()->pubsetbuf(in1buf, FILE_BUFFER_SIZE);
|
|
// read first byte to detect used version
|
|
char version;
|
|
in1.read(&version,1);
|
|
if(!in1.good()){
|
|
return error(res,"problem in reading file", &in1,0,in1buf);
|
|
}
|
|
if(version!=0x02 && version!=0x03 && version!=0x83){
|
|
return error(res,"file does not contain a supported "
|
|
"dbase version", &in1,0,in1buf);
|
|
}
|
|
bool hasMemoLocal = false;
|
|
if(version==0x83){
|
|
return error(res,"memo fields are not supported yet",&in1,0,in1buf);
|
|
hasMemoLocal = true;
|
|
}
|
|
|
|
|
|
// read global header information
|
|
in1.seekg(4);
|
|
uint32_t inNoRecords = readLittleInt32(in1);
|
|
uint16_t headerLength = readLittleInt16(in1);
|
|
uint16_t recordLength = readLittleInt16(in1);
|
|
in1.seekg(32); // jump over the 20 reserved bytes
|
|
// read the field
|
|
char field[32];
|
|
vector<db3AttrType> type;
|
|
while(in1.good() && (in1.tellg() < (headerLength - 1))){
|
|
in1.read(field,32);
|
|
db3AttrType t(field);
|
|
type.push_back(t);
|
|
if(!t.isValid()){
|
|
return error(res,"found invalid field definition",&in1,0,in1buf);
|
|
}
|
|
}
|
|
// read end of header mark
|
|
char mark;
|
|
in1.read(&mark,1);
|
|
if(mark!=0x0d){
|
|
return error(res,"invalid structure found",&in1,0,in1buf);
|
|
}
|
|
// copy header information if this is the first file
|
|
if(out->tellp()==0){
|
|
in1.seekg(0);
|
|
char head[headerLength];
|
|
in1.read(head,headerLength);
|
|
out->write(head, headerLength);
|
|
dbType = type; // overtake type information
|
|
hasMemo = hasMemoLocal;
|
|
} else {
|
|
if(dbType.size()!=type.size()){
|
|
return error(res,"different types in files",&in1,0,in1buf);
|
|
}
|
|
for(size_t i=0;i<type.size();i++){
|
|
if(type[i]!=dbType[i]){
|
|
return error(res,"different types in files",&in1,0,in1buf);
|
|
}
|
|
}
|
|
}
|
|
// copy content for non-Memo
|
|
if(!hasMemo){
|
|
char* buffer = new char[recordLength];
|
|
uint32_t writtenRecords=0;
|
|
while(writtenRecords<inNoRecords && in1.good()){
|
|
in1.read(buffer,recordLength);
|
|
if(in1.good()){
|
|
out->write(buffer,recordLength);
|
|
writtenRecords++;
|
|
}
|
|
}
|
|
delete[] buffer;
|
|
if(writtenRecords != inNoRecords){
|
|
stringstream ss;
|
|
ss << "written " << writtenRecords
|
|
<< " but according to the header it should be "
|
|
<< inNoRecords;
|
|
return error(res,ss.str(),&in1,0,in1buf);
|
|
}
|
|
in1.close();
|
|
noRecords += writtenRecords;
|
|
res->PutAttribute(1,new CcBool(true,true));
|
|
res->PutAttribute(2,new FText(true,""));
|
|
delete[] in1buf;
|
|
return res;
|
|
} else {
|
|
return error(res,"Memo fields are not implemented yet",
|
|
&in1,0,in1buf);
|
|
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
template<class T, class F>
|
|
int db3CollectVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
db3CollectInfo<T>* li = (db3CollectInfo<T>*) local.addr;
|
|
switch(message){
|
|
case OPEN : {
|
|
if(li) {
|
|
delete li;
|
|
local.addr=0;
|
|
}
|
|
F* fn = (F*) args[1].addr;
|
|
if(!fn->IsDefined()){
|
|
return 0;
|
|
}
|
|
ListExpr tt = nl->Second(GetTupleResultType(s));
|
|
local.addr = new db3CollectInfo<T>(args[0],
|
|
fn->GetValue(),tt);
|
|
return 0;
|
|
}
|
|
case REQUEST : {
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE : {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueMapping db3CollectVM[] = {
|
|
db3CollectVMT<CcString,CcString>,
|
|
db3CollectVMT<CcString,FText>,
|
|
db3CollectVMT<FText,CcString>,
|
|
db3CollectVMT<FText,FText>
|
|
};
|
|
|
|
int db3CollectSelect(ListExpr args){
|
|
int n1 = Stream<CcString>::checkType(nl->First(args))?0:2;
|
|
int n2 = CcString::checkType(nl->Second(args))?0:1;
|
|
return n1+n2;
|
|
}
|
|
|
|
OperatorSpec db3CollectSpec(
|
|
" stream({text,string}) x {text, string} -> stream(tuple) ",
|
|
" _ db3collect[_]",
|
|
"Puts some shape files whose names comes from the stream "
|
|
"into a single dbase file. The output is a tuple stream "
|
|
" reporting for each file whether the connect was successful"
|
|
" and in case of an error with an error message.",
|
|
" query getdirectory(\".\") filter[ . endsWith \".dbf\"] "
|
|
"db3Collect['all.db3']"
|
|
);
|
|
|
|
Operator db3CollectOp(
|
|
"db3Collect",
|
|
db3CollectSpec.getStr(),
|
|
4,
|
|
db3CollectVM,
|
|
db3CollectSelect,
|
|
db3CollectTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
24,19 Operator ~noShpRecords~
|
|
|
|
Retrieves the number of records from an shx file indexing
|
|
some shp file. If the shx file is not present, this operator
|
|
counts the number of records by scanning the main shape file.
|
|
|
|
*/
|
|
ListExpr noShpRecordsTM(ListExpr args){
|
|
const string err = "string or text expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err+ " (wrong number of args)");
|
|
}
|
|
if( !CcString::checkType(nl->First(args))
|
|
&& !FText::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
|
|
|
|
template<class F>
|
|
int noShpRecordsVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
F* fn = (F*) args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = (CcInt*) result.addr;
|
|
if(!fn->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
string name = fn->GetValue();
|
|
string n2 = name;
|
|
stringutils::toLower(n2);
|
|
string shpname;
|
|
string shxname;
|
|
|
|
if(stringutils::endsWith(n2,".shx")){
|
|
shxname = name;
|
|
shpname = name.substr(0,name.length()-4)+".shp";
|
|
} else if(stringutils::endsWith(n2,"shp")){
|
|
shxname = name.substr(0,name.length()-4)+".shx";
|
|
shpname = name;
|
|
} else {
|
|
shxname = name+".shx";
|
|
shpname = name+".shp";
|
|
}
|
|
// try to get the record number without scanning the shapefile
|
|
ifstream shxin(shxname.c_str(),ios::binary);
|
|
if(shxin.good()){
|
|
shxin.seekg(0,ios::end);
|
|
uint32_t len = shxin.tellg();
|
|
shxin.close();
|
|
if(len>=100){ // assumed to be a shx file
|
|
res->Set(true, (len-100)/8);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
cerr << "no shx file found, scan shp file" << endl;
|
|
|
|
// using shx file was not successful, scan shp file
|
|
ifstream shpin(shpname.c_str(),ios::binary);
|
|
if(!shpin.good()){ // cannot open file
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
char* buffer = new char[FILE_BUFFER_SIZE];
|
|
shpin.rdbuf()->pubsetbuf(buffer, FILE_BUFFER_SIZE);
|
|
uint32_t code = readBigInt32(shpin);
|
|
if(!shpin.good() || (code!=9994)){ // not a shape file
|
|
shpin.close();
|
|
delete[] buffer;
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
shpin.seekg(24);
|
|
uint64_t len = readBigInt32(shpin);
|
|
if(!shpin.good()){
|
|
shpin.close();
|
|
delete[] buffer;
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
len = len*2; // length is given in 16 bit words
|
|
shpin.seekg(100); // jump over header
|
|
uint64_t pos = 100;
|
|
uint32_t count = 0;
|
|
while(pos < len && shpin.good()){
|
|
count++;
|
|
uint32_t recNo = readBigInt32(shpin);
|
|
uint32_t cl = readBigInt32(shpin);
|
|
if(recNo!=count){
|
|
cerr << "found wrong numbering in shape file" << endl
|
|
<< " should be " << count << endl
|
|
<< " but is actually " << recNo << endl;
|
|
cerr << "content length is " << cl << endl;
|
|
}
|
|
shpin.seekg(2*cl,ios::cur);
|
|
pos += 2*(4+cl);
|
|
}
|
|
shpin.close();
|
|
delete[] buffer;
|
|
res->Set(true,count);
|
|
return 0;
|
|
}
|
|
|
|
|
|
ValueMapping noShpRecordsVM[] = {
|
|
noShpRecordsVMT<CcString>,
|
|
noShpRecordsVMT<FText>
|
|
};
|
|
|
|
|
|
int noShpRecordsSelect(ListExpr args){
|
|
return CcString::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
OperatorSpec noShpRecordsSpec(
|
|
" {string, text} -> int ",
|
|
"noShpRecords(_) ",
|
|
"Retrieves the number of records contained in a shape file. "
|
|
"If the corresponsing index file is present, it will be "
|
|
"used to determine the number of records in constant time. "
|
|
"If the index file is not present, the shape file itself "
|
|
"is scanned to get this number.",
|
|
"query noRecords('Kinos.shp')"
|
|
|
|
);
|
|
|
|
Operator noShpRecordsOp(
|
|
"noShpRecords",
|
|
noShpRecordsSpec.getStr(),
|
|
2,
|
|
noShpRecordsVM,
|
|
noShpRecordsSelect,
|
|
noShpRecordsTM
|
|
);
|
|
|
|
|
|
/*
|
|
24.10 Operator ~noDB3Records~
|
|
|
|
This operator returns the number of records within a
|
|
d-base III file. If the second argument is given and
|
|
TRUE, the file size is checked against the stored number.
|
|
|
|
*/
|
|
|
|
ListExpr noDB3RecordsTM(ListExpr args){
|
|
string err = "{string, text} [ x bool] expected";
|
|
if(!nl->HasLength(args,1) && !nl->HasLength(args,2)){
|
|
return listutils::typeError(err+ " (wrong number of args)");
|
|
}
|
|
if( !CcString::checkType(nl->First(args))
|
|
&& !FText::checkType(nl->First(args))){
|
|
return listutils::typeError(err + " (first arg is neither a string "
|
|
"nor a text)");
|
|
}
|
|
if(nl->HasLength(args,1)){
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList(nl->BoolAtom(false)),
|
|
listutils::basicSymbol<CcInt>());
|
|
}
|
|
if(!CcBool::checkType(nl->Second(args))){
|
|
return listutils::typeError(err + " (second arg ist not a bool)");
|
|
}
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
|
|
|
|
|
|
template<class F>
|
|
int noDB3RecordsVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
F* f = (F*) args[0].addr;
|
|
CcBool* c = (CcBool*) args[1].addr;
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = (CcInt*) result.addr;
|
|
|
|
if(!f->IsDefined() || !c->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
string fn = f->GetValue();
|
|
ifstream in(fn.c_str(), ios::binary);
|
|
if(!in){
|
|
cerr << "could not open file " << f->GetValue() << endl;
|
|
res->Set(false,0);
|
|
return 0;
|
|
}
|
|
unsigned char version;
|
|
in.read((char*)(&version),1);
|
|
if(version!=0x02 && version!=0x03 && version!=0x83){
|
|
cerr << "file " << f->GetValue() << " seems not to be a dbase file"
|
|
<< endl;
|
|
res->Set(false,0);
|
|
return 0;
|
|
}
|
|
in.seekg(4);
|
|
uint32_t noRecords = readLittleInt32(in);
|
|
if(!in.good()){
|
|
cerr << "problem in reading file" << endl;
|
|
return 0;
|
|
}
|
|
if(!c->GetValue()){
|
|
res->Set(true,noRecords);
|
|
return 0;
|
|
}
|
|
uint16_t headerLength = readLittleInt16(in);
|
|
uint16_t recordSize = readLittleInt16(in);
|
|
in.seekg(0,ios::end);
|
|
size_t fileLength = in.tellg();
|
|
if(fileLength != headerLength + noRecords * recordSize &&
|
|
fileLength != headerLength + 1 + noRecords * recordSize ){
|
|
// may be there is a 0x1A end marker or not
|
|
cerr << "real file length is " << fileLength << endl;
|
|
cerr << "expected file length is "
|
|
<< (headerLength + noRecords * recordSize) << endl;
|
|
|
|
cerr << "headerLength = " << headerLength << endl;
|
|
cerr << "recordSize = " << recordSize << endl;
|
|
cerr << "noRecords = " << noRecords << endl;
|
|
|
|
res->Set(true,-1 * (int)noRecords);
|
|
} else {
|
|
res->Set(true,noRecords);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping noDB3RecordsVM[] = {
|
|
noDB3RecordsVMT<CcString>,
|
|
noDB3RecordsVMT<FText>
|
|
};
|
|
|
|
int noDB3RecordsSelect(ListExpr args){
|
|
return CcString::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
OperatorSpec noDB3RecordsSpec(
|
|
"{string, text} [x bool] -> int",
|
|
"noDB3Records(filename, check)",
|
|
"Retrieves the number of records stored in a db3 file. "
|
|
"If the optional argument is given and true, the result "
|
|
"read from the file is compared with the expected file "
|
|
"size. If the computed file size and the real file size "
|
|
"differ, the result will be negated.",
|
|
"query noDB3Records('ten.dbf', TRUE) "
|
|
);
|
|
|
|
Operator noDB3RecordsOp(
|
|
"noDB3Records",
|
|
noDB3RecordsSpec.getStr(),
|
|
2,
|
|
noDB3RecordsVM,
|
|
noDB3RecordsSelect,
|
|
noDB3RecordsTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
4.27 Operator ~createShx~
|
|
|
|
This operator scans a shape file and creates a idx file
|
|
from it.
|
|
|
|
4.27.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr createShxTM(ListExpr args){
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError("two erguments expected");
|
|
}
|
|
if(!CcString::checkType(nl->First(args))
|
|
&&!FText::checkType(nl->First(args))){
|
|
return listutils::typeError("first argument neihter a string nor a text");
|
|
}
|
|
if(!CcString::checkType(nl->Second(args))
|
|
&&!FText::checkType(nl->Second(args))){
|
|
return listutils::typeError("Second argument neihter a string nor a text");
|
|
}
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
|
|
|
|
/*
|
|
4.27.2 Value Mapping
|
|
|
|
*/
|
|
template<class S, class T>
|
|
int createShxVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
S* src = (S*) args[0].addr;
|
|
T* dest = (T*) args[1].addr;
|
|
if(!src->IsDefined() || !dest->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
ifstream in(src->GetValue().c_str(), ios::binary);
|
|
if(!in){ // problem in opening input file
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
ofstream out(dest->GetValue().c_str(), ios::binary);
|
|
if(!out){ // problem in opening output file
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
char* inbuf = new char[FILE_BUFFER_SIZE];
|
|
char* outbuf = new char[FILE_BUFFER_SIZE];
|
|
in.rdbuf()->pubsetbuf(inbuf,FILE_BUFFER_SIZE);
|
|
out.rdbuf()->pubsetbuf(outbuf,FILE_BUFFER_SIZE);
|
|
|
|
uint32_t code = readBigInt32(in);
|
|
if(!in.good() || code!=9994){ // read error or not a shpe file
|
|
res->Set(true,false);
|
|
delete[] inbuf;
|
|
delete[] outbuf;
|
|
return 0;
|
|
}
|
|
//in.seekg(24);
|
|
//uint32_t length = readBigInt32(in);
|
|
in.seekg(0);
|
|
char header[100];
|
|
in.read(header,100);
|
|
if(!in){
|
|
res->Set(true,false);
|
|
delete[] inbuf;
|
|
delete[] outbuf;
|
|
return 0;
|
|
}
|
|
// copy header into shx file
|
|
out.write(header,100);
|
|
uint32_t offset = 100;
|
|
while(in.good() && !in.eof() ){
|
|
uint32_t rn; // record number
|
|
in.read((char*)&rn,4); // read over record number
|
|
uint32_t cl = readBigInt32(in); // content length
|
|
if(in.good()){
|
|
WinUnix::writeBigEndian(out,offset/2);
|
|
WinUnix::writeBigEndian(out,cl);
|
|
}
|
|
offset += 8 + 2*cl;
|
|
in.seekg(cl*2, ios_base::cur); // jump over content
|
|
}
|
|
uint32_t shxLen = out.tellp() / 2; // in 16 bit words
|
|
out.seekp(24);
|
|
WinUnix::writeBigEndian(out,shxLen);
|
|
in.close();
|
|
out.close();
|
|
res->Set(true,true);
|
|
delete[] inbuf;
|
|
delete[] outbuf;
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping createShxVM[] = {
|
|
createShxVMT<CcString, CcString>,
|
|
createShxVMT<CcString, FText>,
|
|
createShxVMT<FText, CcString>,
|
|
createShxVMT<FText, FText>
|
|
};
|
|
|
|
int createShxSelect(ListExpr args){
|
|
int n1 = CcString::checkType(nl->First(args))?0:2;
|
|
int n2 = CcString::checkType(nl->Second(args))?0:1;
|
|
return n1+n2;
|
|
}
|
|
|
|
OperatorSpec createShxSpec(
|
|
"{string,text} x {string,text} -> bool ",
|
|
"createShx(_,_)",
|
|
"Creates an shx file from a shape file.",
|
|
"query createShx('Kinos.shp', 'Kinos.shx')"
|
|
);
|
|
|
|
Operator createShxOp(
|
|
"createShx",
|
|
createShxSpec.getStr(),
|
|
4,
|
|
createShxVM,
|
|
createShxSelect,
|
|
createShxTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
|
|
25.9 Operator ~extractShpPart~
|
|
|
|
25.9.1 Type Mapping
|
|
|
|
The operator expects 4 arguments.
|
|
|
|
* the file name
|
|
|
|
* the index of the first record
|
|
|
|
* the index of the last record
|
|
|
|
* a boolean value determining whether an index file should be created too
|
|
|
|
* a boolean value for allowing a scan if necessary
|
|
|
|
* a string or text defining the deistination file
|
|
|
|
Usually, this operator will use the shx file to determine the
|
|
position of the first record to extract. The shape file is
|
|
scanned from this position until the end is reached or the
|
|
maximum number of records have been extracted.
|
|
If there is no shx file or the shape file size exceeds the
|
|
limit of 4GB (maximum adressable file position in the shx file),
|
|
the last boolean argument determines whether a scan of the
|
|
original shape file from the beginning is allowed.
|
|
|
|
*/
|
|
|
|
ListExpr extractShpPartTM(ListExpr args){
|
|
string err = "{string,text} x int x int x bool x bool "
|
|
"x {string, text} expected";
|
|
if(!nl->HasLength(args,6)){
|
|
return listutils::typeError(err + " (wrong number of args)");
|
|
}
|
|
if( !CcString::checkType(nl->First(args))
|
|
&& !FText::checkType(nl->First(args))){
|
|
return listutils::typeError(err+ " (first arg is neither a string "
|
|
"nor a text");
|
|
}
|
|
if(!CcInt::checkType(nl->Second(args))){
|
|
return listutils::typeError(err+ " (second arg is not an int");
|
|
}
|
|
if(!CcInt::checkType(nl->Third(args))){
|
|
return listutils::typeError(err+ " (Third arg is not an int");
|
|
}
|
|
if(!CcBool::checkType(nl->Fourth(args))){
|
|
return listutils::typeError(err+ " (4th arg is not a bool");
|
|
}
|
|
if(!CcBool::checkType(nl->Fifth(args))){
|
|
return listutils::typeError(err+ " (5th arg is not a bool");
|
|
}
|
|
if( !CcString::checkType(nl->Sixth(args))
|
|
&& !FText::checkType(nl->Sixth(args))){
|
|
return listutils::typeError(err+ " (6th arg is neither a string "
|
|
"nor a text");
|
|
}
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
|
|
|
|
|
|
void updateBox(const uint32_t type, char* buffer,
|
|
uint32_t contentLength,
|
|
double& minX, double& minY,
|
|
double& maxX, double& maxY,
|
|
bool& first){
|
|
|
|
if(contentLength==0){
|
|
return;
|
|
}
|
|
contentLength = contentLength*2; // in bytes
|
|
|
|
double lminX;
|
|
double lminY;
|
|
double lmaxX;
|
|
double lmaxY;
|
|
// determine values for lminX and so on from the block
|
|
bool le = WinUnix::isLittleEndian();
|
|
const uint32_t btype = *(reinterpret_cast<uint32_t*>(buffer));
|
|
if(btype==0){ // a null shape does not contibute to the bbox
|
|
return;
|
|
}
|
|
if(btype!=type){ // should not happen
|
|
cerr << "the record shape type does not corresponds "
|
|
"to the file shape type" << endl;
|
|
return;
|
|
}
|
|
switch(type){
|
|
case 1: {
|
|
if(contentLength<20){
|
|
cerr << "found invalid content length for a point" << endl;
|
|
return;
|
|
}
|
|
uint64_t ix = * (reinterpret_cast<uint64_t*>(buffer+4));
|
|
uint64_t iy = * (reinterpret_cast<uint64_t*>(buffer+12));
|
|
if(!le){
|
|
ix = WinUnix::convertEndian(ix);
|
|
iy = WinUnix::convertEndian(iy);
|
|
}
|
|
void* d = &ix;
|
|
lminX = *(reinterpret_cast<double*>(d));
|
|
lmaxX = lminX;
|
|
d = &iy;
|
|
lminY = *(reinterpret_cast<double*>(d));
|
|
lmaxY = lminY;
|
|
break;
|
|
}
|
|
case 3:
|
|
case 5:
|
|
case 8: {
|
|
if(contentLength<36){
|
|
cerr << "found invalid content length "
|
|
"for a spatial object" << endl;
|
|
return;
|
|
}
|
|
uint64_t ix1 = *(reinterpret_cast<uint64_t*>(buffer+4));
|
|
uint64_t iy1 = *(reinterpret_cast<uint64_t*>(buffer+12));
|
|
uint64_t ix2 = *(reinterpret_cast<uint64_t*>(buffer+20));
|
|
uint64_t iy2 = *(reinterpret_cast<uint64_t*>(buffer+28));
|
|
if(!le){
|
|
ix1 = WinUnix::convertEndian(ix1);
|
|
ix2 = WinUnix::convertEndian(ix2);
|
|
iy1 = WinUnix::convertEndian(iy1);
|
|
iy2 = WinUnix::convertEndian(iy2);
|
|
}
|
|
void* d = &ix1;
|
|
lminX = *( reinterpret_cast<double*>(d));
|
|
d = &iy1;
|
|
lminY = *( reinterpret_cast<double*>(d));
|
|
d=&ix2;
|
|
lmaxX = *( reinterpret_cast<double*>(d));
|
|
d = &iy2;
|
|
lmaxY = *( reinterpret_cast<double*>(d));
|
|
break;
|
|
}
|
|
default: assert(false); // type not supported
|
|
}
|
|
|
|
if(first){
|
|
minX = lminX;
|
|
minY = lminY;
|
|
maxX = lmaxX;
|
|
maxY = lmaxY;
|
|
first = false;
|
|
} else {
|
|
minX = min(minX,lminX);
|
|
minY = min(minY,lminY);
|
|
maxX = max(maxX,lmaxX);
|
|
maxY = max(maxY,lmaxY);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int extractShpPart( const string& shpFile,
|
|
uint32_t i1, uint32_t i2,
|
|
bool cx, bool as,
|
|
const string& targetFile){
|
|
|
|
if(!stringutils::endsWith(shpFile,".shp" )){
|
|
cerr << "source file name does not end with .shp" << endl;
|
|
return 0;
|
|
}
|
|
if(!stringutils::endsWith(targetFile,".shp")){
|
|
cerr << "target file name does not end with .shp" << endl;
|
|
return 0;
|
|
}
|
|
|
|
if(i1>i2){
|
|
swap(i1,i2);
|
|
}
|
|
|
|
i1++; // force counting at record 1 as in shp file
|
|
i2++;
|
|
|
|
|
|
// try to open the file and determine the file size
|
|
ifstream inshp(shpFile.c_str(), ios::binary);
|
|
if(!inshp){ // shape file could not be opened
|
|
cerr << "could not open source shape file" << endl;
|
|
return 0;
|
|
}
|
|
|
|
// check for shape file of supported type
|
|
uint32_t code = readBigInt32(inshp);
|
|
if(!inshp.good() || code != 9994){
|
|
cerr << "source file not recognized to be a shape file" << endl;
|
|
inshp.close();
|
|
return 0;
|
|
}
|
|
inshp.seekg(32);
|
|
uint32_t type = readLittleInt32(inshp);
|
|
if(!inshp.good()){
|
|
cerr << "could not extract type of the shape file" << endl;
|
|
inshp.close();
|
|
return 0;
|
|
}
|
|
if(type!=1 && type !=3 && type!=5 && type!=8){
|
|
cerr << "unsupported type " << type << endl;
|
|
inshp.close();
|
|
return 0;
|
|
}
|
|
// determine size of the file
|
|
uint64_t one = 1;
|
|
uint64_t maxsize = 2*((one << 32)-1);
|
|
|
|
|
|
inshp.seekg(0,ios::end);
|
|
uint64_t filesize = inshp.tellg();
|
|
|
|
|
|
bool indexAvailable = filesize < maxsize;
|
|
|
|
|
|
uint64_t firstPos = 0;
|
|
|
|
if(indexAvailable){
|
|
ifstream inshx((shpFile.substr(0,shpFile.length()-4)+".shx").c_str(),
|
|
ios::binary);
|
|
if(!inshx){
|
|
indexAvailable = false;
|
|
} else {
|
|
uint64_t i1pos = 100 + 8*(i1-1);
|
|
inshx.seekg(i1pos);
|
|
if(!inshx.good()){ // outside the index file
|
|
inshp.close();
|
|
inshx.close();
|
|
return 0;
|
|
}
|
|
firstPos = readBigInt32(inshx);
|
|
if(!inshx.good()){ // after the last position
|
|
inshp.close();
|
|
inshx.close();
|
|
return 0;
|
|
}
|
|
inshx.close(); // index file is processed
|
|
}
|
|
}
|
|
if(indexAvailable){
|
|
firstPos = firstPos * 2; // convert 16Bit to byte
|
|
}
|
|
|
|
char* buffer = new char[FILE_BUFFER_SIZE];
|
|
inshp.rdbuf()->pubsetbuf(buffer,FILE_BUFFER_SIZE);
|
|
|
|
|
|
if(!indexAvailable){
|
|
if(!as){
|
|
// impossible to determine the start position
|
|
// without scanning the main file
|
|
cerr << "index not available and scan is not allowed" << endl;
|
|
inshp.close();
|
|
delete[] buffer;
|
|
return 0;
|
|
}
|
|
inshp.seekg(100);
|
|
|
|
// search record i1 by scanning the main file
|
|
uint32_t record = 1;
|
|
while(inshp.good() && !inshp.eof() && record < i1){
|
|
uint32_t rec = readBigInt32(inshp);
|
|
if(rec!=record){
|
|
cerr << "wrong record numbering in shape file found" << endl;
|
|
}
|
|
uint32_t cl = readBigInt32(inshp); // content length
|
|
// jump over content
|
|
inshp.seekg(cl*2,ios::cur);
|
|
record++;
|
|
}
|
|
if(!inshp.good() || record!=i1){
|
|
// index outside file
|
|
cerr << "first index outside the file" << endl;
|
|
inshp.close();
|
|
delete[] buffer;
|
|
return 0;
|
|
}
|
|
firstPos = inshp.tellg();
|
|
|
|
}
|
|
// ok, the first index is in the file, at a first step, we
|
|
// copy the header of the main file into the target file
|
|
// and return the the position of i1
|
|
ofstream outshp(targetFile.c_str(),ios::binary);
|
|
ofstream outshx;
|
|
char* outBuffer2 = 0;
|
|
if(cx){
|
|
outshx.open((targetFile.substr(0,targetFile.length()-4)+".shx").c_str(),
|
|
ios::binary);
|
|
|
|
}
|
|
if(!outshp){
|
|
cerr << "could not open output file" << endl;
|
|
delete[] buffer;
|
|
return 0;
|
|
}
|
|
char* outBuffer1 = new char[FILE_BUFFER_SIZE];
|
|
outshp.rdbuf()->pubsetbuf(outBuffer1,FILE_BUFFER_SIZE);
|
|
|
|
if(cx && outshx.good()){
|
|
outBuffer2 = new char[FILE_BUFFER_SIZE];
|
|
outshx.rdbuf()->pubsetbuf(outBuffer2, FILE_BUFFER_SIZE);
|
|
}
|
|
|
|
// copy header
|
|
inshp.seekg(0);
|
|
char header[100];
|
|
inshp.read(header,100);
|
|
outshp.write(header,100);
|
|
if(outBuffer2){
|
|
outshx.write(header,100);
|
|
}
|
|
|
|
inshp.seekg(firstPos);
|
|
|
|
double minX =0;
|
|
double maxX =0;
|
|
double minY =0;
|
|
double maxY = 0;
|
|
bool first = true;
|
|
uint32_t count = 0;
|
|
|
|
while(inshp.good() && !inshp.eof() && i1 <= i2){
|
|
// read next element from shape file
|
|
uint32_t rec = readBigInt32(inshp);
|
|
if(rec!=i1){
|
|
cerr << "found invalid numbering in shape file" << endl;
|
|
}
|
|
uint32_t cl = readBigInt32(inshp);
|
|
if(inshp.good()){
|
|
char* recbuf = new char[2*cl];
|
|
inshp.read(recbuf,cl*2);
|
|
if(inshp.good()){
|
|
uint32_t p = outshp.tellp() / 2;
|
|
if(outBuffer2){ // write shx file if required
|
|
WinUnix::writeBigEndian(outshx,p);
|
|
WinUnix::writeBigEndian(outshx,cl);
|
|
}
|
|
count++;
|
|
WinUnix::writeBigEndian(outshp, count);
|
|
WinUnix::writeBigEndian(outshp, cl);
|
|
outshp.write(recbuf,2*cl);
|
|
updateBox(type,recbuf,cl,minX,minY,maxX,maxY,first);
|
|
i1++;
|
|
}
|
|
delete[] recbuf;
|
|
}
|
|
}
|
|
|
|
// update header information
|
|
uint32_t fileLength = outshp.tellp()/2;
|
|
outshp.seekp(24);
|
|
WinUnix::writeBigEndian(outshp,fileLength);
|
|
outshp.seekp(36);
|
|
WinUnix::writeLittle64(outshp,minX);
|
|
WinUnix::writeLittle64(outshp,minY);
|
|
WinUnix::writeLittle64(outshp,maxX);
|
|
WinUnix::writeLittle64(outshp,maxY);
|
|
outshp.close();
|
|
delete[] outBuffer1;
|
|
if(outBuffer2){
|
|
fileLength = outshx.tellp()/2;
|
|
outshx.seekp(24);
|
|
WinUnix::writeBigEndian(outshx,fileLength);
|
|
outshx.seekp(36);
|
|
WinUnix::writeLittle64(outshx,minX);
|
|
WinUnix::writeLittle64(outshx,minY);
|
|
WinUnix::writeLittle64(outshx,maxX);
|
|
WinUnix::writeLittle64(outshx,maxY);
|
|
outshx.close();
|
|
delete[] outBuffer2;
|
|
}
|
|
delete[] buffer;
|
|
return count;
|
|
}
|
|
|
|
|
|
template<class T, class F>
|
|
int extractShpPartVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
T* fn = (T*) args[0].addr;
|
|
CcInt* i1 = (CcInt*) args[1].addr;
|
|
CcInt* i2 = (CcInt*) args[2].addr;
|
|
CcBool* cx = (CcBool*) args[3].addr;
|
|
CcBool* as = (CcBool*) args[4].addr;
|
|
F* fnt = (F*) args[5].addr;
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = (CcInt*) result.addr;
|
|
|
|
|
|
if( !fn->IsDefined() || !i1->IsDefined()
|
|
|| !i2->IsDefined() || !cx->IsDefined()
|
|
|| !as->IsDefined() || !fnt->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
res->Set(true, extractShpPart( fn->GetValue(),
|
|
i1->GetValue(),
|
|
i2->GetValue(),
|
|
cx->GetValue(),
|
|
as->GetValue(),
|
|
fnt->GetValue()));
|
|
return 0;
|
|
}
|
|
|
|
|
|
ValueMapping extractShpPartVM[] = {
|
|
extractShpPartVMT<CcString, CcString>,
|
|
extractShpPartVMT<CcString, FText>,
|
|
extractShpPartVMT<FText, CcString>,
|
|
extractShpPartVMT<FText, FText>
|
|
};
|
|
|
|
int extractShpPartSelect(ListExpr args){
|
|
int n1 = CcString::checkType(nl->First(args))?0:2;
|
|
int n2 = CcString::checkType(nl->Sixth(args))?0:1;
|
|
return n1+n2;
|
|
}
|
|
|
|
OperatorSpec extractShpPartSpec(
|
|
"{string,text} x int x int x bool x bool x {string,text} -> int",
|
|
"extractSphPart(source, minRecord, maxRecord, createShx, allowScan, target)",
|
|
"Extracts a part of a shape file and stores this part into another "
|
|
"shape file. The first argument is the name of the source file "
|
|
"including the .shp extension. The secnnd and the third argument "
|
|
"determine the range of records taht should be extracted. Counting of "
|
|
"records starts with zero."
|
|
"The first boolean argument determines whether an index (shx) file should "
|
|
"be created for the extracted part. "
|
|
"In the normal case, this operator uses an index (shx) file "
|
|
"to determine the begin of the first record within the shape file."
|
|
"This is impossible if the shape file has a size of more than 8GB or "
|
|
"of course if no index file is present. "
|
|
"In such a case, a scan within the main file up to the first "
|
|
"record to extract is reqwired. Because this is expensive, the fifth "
|
|
"argument controls whether such a scan is allowed. "
|
|
"The last argument is the name of the target file including the shp "
|
|
"extension.",
|
|
"query extractShpPart('buildings.shp',1000, 1500, TRUE, "
|
|
"TRUE, 'buildings_1000_1500')"
|
|
);
|
|
|
|
|
|
Operator extractShpPartOp(
|
|
"extractShpPart",
|
|
extractShpPartSpec.getStr(),
|
|
4,
|
|
extractShpPartVM,
|
|
extractShpPartSelect,
|
|
extractShpPartTM
|
|
);
|
|
|
|
|
|
/*
|
|
24.93 Operator ~extractDB3Part~
|
|
|
|
This operator extracts a part of a db3 file into a new db3 file.
|
|
|
|
24.93.1 Type Mapping
|
|
|
|
Arguments are:
|
|
|
|
* the source file name
|
|
|
|
* the start index
|
|
|
|
* the end index
|
|
|
|
* the target file name
|
|
|
|
*/
|
|
ListExpr extractDB3PartTM(ListExpr args){
|
|
string err="{string, text} x int x int x {string, text} expected";
|
|
if(!nl->HasLength(args,4)){
|
|
return listutils::typeError(err + " (wrong number of arguments");
|
|
}
|
|
if( !CcString::checkType(nl->First(args))
|
|
&& !FText::checkType(nl->First(args))){
|
|
return listutils::typeError(err + " (first arg is neither a string"
|
|
" nor a text)");
|
|
}
|
|
if(!CcInt::checkType(nl->Second(args))){
|
|
return listutils::typeError(err + " (second arg is not an int)");
|
|
}
|
|
if(!CcInt::checkType(nl->Third(args))){
|
|
return listutils::typeError(err + " (third arg is not an int)");
|
|
}
|
|
if( !CcString::checkType(nl->Fourth(args))
|
|
&& !FText::checkType(nl->Fourth(args))){
|
|
return listutils::typeError(err + " (fourth arg is neither a string"
|
|
" nor a text)");
|
|
}
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
|
|
|
|
|
|
int extractDB3Part(string source, int first, int last, string target){
|
|
|
|
if(first>last){
|
|
swap(first,last);
|
|
}
|
|
if(first<0){
|
|
cerr << "invalid first tuple" << endl;
|
|
return 0;
|
|
}
|
|
ifstream in(source.c_str(),ios::binary);
|
|
if(!in.good()){
|
|
cerr << "could not open source file " << source << endl;
|
|
return 0;
|
|
}
|
|
char* inBuffer = new char[FILE_BUFFER_SIZE];
|
|
in.rdbuf()->pubsetbuf(inBuffer,FILE_BUFFER_SIZE);
|
|
unsigned char version;
|
|
in.read((char*)(&version),1);
|
|
if(!in.good() || (version !=0x02 && version!=0x03 && version!=0x83)){
|
|
cerr << "file " << source << " is not a supported d-base file" << endl;
|
|
delete[] inBuffer;
|
|
return 0;
|
|
}
|
|
if(version==0x83){
|
|
cerr << "memo field support not implemented yet" << endl;
|
|
delete[] inBuffer;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// determine number of records in file
|
|
in.seekg(4, ios::beg);
|
|
uint32_t noRecords = readLittleInt32(in);
|
|
if(!in.good() || (uint32_t)first>=noRecords){
|
|
cerr << "too less records in file " << endl;
|
|
delete[] inBuffer;
|
|
return 0;
|
|
}
|
|
// determine length of the header
|
|
uint16_t headerLength = readLittleInt16(in);
|
|
uint16_t recordLength = readLittleInt16(in);
|
|
|
|
if((uint64_t)last>=noRecords){
|
|
last = noRecords - 1;
|
|
}
|
|
// copy db3 header
|
|
char* header = new char[headerLength];
|
|
in.seekg(0,ios::beg);
|
|
in.read(header, headerLength);
|
|
// time to open the output file
|
|
ofstream out(target.c_str(), ios::binary);
|
|
if(!out){
|
|
cerr << "could not open output file";
|
|
delete[] inBuffer;
|
|
delete[] header;
|
|
return 0;
|
|
}
|
|
// write header to output file
|
|
char* outBuffer = new char[FILE_BUFFER_SIZE];
|
|
out.rdbuf()->pubsetbuf(outBuffer, FILE_BUFFER_SIZE);
|
|
|
|
out.write(header, headerLength);
|
|
|
|
uint64_t bytes = ((last + 1) - first) * recordLength;
|
|
// navigate to start position in in file
|
|
in.seekg(first*recordLength, ios::cur);
|
|
size_t bs = 16*1024;
|
|
char buffer[bs]; // copy buffer by buffer
|
|
while(bytes>0 && in.good() && !in.eof()){
|
|
size_t read = bytes>bs?bs:bytes;
|
|
in.read(buffer,read);
|
|
out.write(buffer,read);
|
|
bytes -= read;
|
|
}
|
|
|
|
if(bytes>0){
|
|
cerr << "error in copying data";
|
|
delete[] inBuffer;
|
|
delete[] outBuffer;
|
|
delete[] header;
|
|
return 0;
|
|
}
|
|
// update record number in target file
|
|
out.seekp(4);
|
|
noRecords = (last+1) - first;
|
|
WinUnix::writeLittleEndian(out,noRecords);
|
|
in.close();
|
|
out.close();
|
|
delete[] inBuffer;
|
|
delete[] outBuffer;
|
|
delete[] header;
|
|
return noRecords;
|
|
}
|
|
|
|
|
|
template<class T, class F>
|
|
int extractDB3PartVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
T* fn = (T*) args[0].addr;
|
|
CcInt* i1 = (CcInt*) args[1].addr;
|
|
CcInt* i2 = (CcInt*) args[2].addr;
|
|
F* fnt = (F*) args[3].addr;
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = (CcInt*) result.addr;
|
|
|
|
|
|
if( !fn->IsDefined() || !i1->IsDefined()
|
|
|| !i2->IsDefined() || !fnt->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
res->Set(true, extractDB3Part( fn->GetValue(),
|
|
i1->GetValue(),
|
|
i2->GetValue(),
|
|
fnt->GetValue()));
|
|
return 0;
|
|
}
|
|
|
|
|
|
ValueMapping extractDB3PartVM[] = {
|
|
extractDB3PartVMT<CcString, CcString>,
|
|
extractDB3PartVMT<CcString, FText>,
|
|
extractDB3PartVMT<FText, CcString>,
|
|
extractDB3PartVMT<FText, FText>
|
|
};
|
|
|
|
int extractDB3PartSelect(ListExpr args){
|
|
int n1 = CcString::checkType(nl->First(args))?0:2;
|
|
int n2 = CcString::checkType(nl->Fourth(args))?0:1;
|
|
return n1+n2;
|
|
}
|
|
|
|
|
|
OperatorSpec extractDB3PartSpec(
|
|
"{string, text} x int x int x {string, text} -> int",
|
|
"extractDB3Part(source, first, last, target)",
|
|
"Extracts a portion given be first and last from a "
|
|
"d-Base III file and stores this part into a "
|
|
"new D-Base II file. As usual counting of records "
|
|
"starts with zero",
|
|
"query extractDB3Part('ten.dbf', 2,8, 'ten_2_8.dbf')"
|
|
);
|
|
|
|
|
|
Operator extractDB3PartOp(
|
|
"extractDB3Part",
|
|
extractDB3PartSpec.getStr(),
|
|
4,
|
|
extractDB3PartVM,
|
|
extractDB3PartSelect,
|
|
extractDB3PartTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
24.29 Operator ~splitShp~
|
|
|
|
This operator splits a shape file into a series of shape files
|
|
having at most a certain number of elements stored.
|
|
|
|
*/
|
|
ListExpr splitShpTM(ListExpr args){
|
|
string err = "{string,text} x int [x bool] expected";
|
|
if(!nl->HasLength(args,2) && !nl->HasLength(args,3)){
|
|
return listutils::typeError(err + " (wrong number of arguments)");
|
|
}
|
|
if( !CcString::checkType(nl->First(args))
|
|
&& !FText::checkType(nl->First(args))){
|
|
return listutils::typeError(err + " (first arg is neither a "
|
|
"string nor a text)");
|
|
}
|
|
if(!CcInt::checkType(nl->Second(args))){
|
|
return listutils::typeError(err + " (second arg in not an int)");
|
|
}
|
|
if(nl->HasLength(args,2)){
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList(nl->BoolAtom(false)),
|
|
listutils::basicSymbol<CcInt>());
|
|
}
|
|
if(!CcBool::checkType(nl->Third(args))){
|
|
return listutils::typeError(err + " ( third arg is not a bool)");
|
|
}
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
|
|
|
|
template<class T>
|
|
int splitShpVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = (CcInt*) result.addr;
|
|
T* fn = (T*) args[0].addr;
|
|
CcInt* norecs = (CcInt*) args[1].addr;
|
|
CcBool* cxs = (CcBool*) args[2].addr;
|
|
|
|
if(!fn->IsDefined() || !norecs->IsDefined() || !cxs->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
bool cx = cxs->GetValue();
|
|
int maxnorec = norecs->GetValue();
|
|
if(maxnorec < 1){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
string f = fn->GetValue();
|
|
if(!stringutils::endsWith(f,".shp")){
|
|
cerr << "invalid file name, missing extension shp" << endl;
|
|
res->Set(true,0);
|
|
return 0;
|
|
}
|
|
|
|
ifstream in(f.c_str(), ios::binary);
|
|
if(!in){
|
|
cerr << "could not open file " << f << endl;
|
|
return 0;
|
|
}
|
|
in.seekg(0,ios::end);
|
|
streampos fileSize = in.tellg();
|
|
if(fileSize < 100){
|
|
cerr << "invalid file (too short)" << endl;
|
|
res->Set(true,0);
|
|
return 0;
|
|
}
|
|
in.seekg(0,ios::beg);
|
|
// read some header information
|
|
uint32_t code = readBigInt32(in);
|
|
if(code!=9994){
|
|
cerr << "file " << f << " is not a shape file" << endl;
|
|
res->Set(true,0);
|
|
return 0;
|
|
}
|
|
|
|
in.seekg(24);
|
|
streampos fs = readBigInt32(in);
|
|
|
|
if(fs*2 != fileSize){
|
|
cerr << "invalid file size stored, give up" << endl;
|
|
res->Set(true,0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
in.seekg(32);
|
|
uint32_t type = readLittleInt32(in);
|
|
if(type!=1 && type!=3 && type!=5 && type!=8){
|
|
cerr << "unsupported shape type " << type << endl;
|
|
res->Set(true,0);
|
|
return 0;
|
|
}
|
|
// header information ok, get the whole header
|
|
char* inBuffer = new char[FILE_BUFFER_SIZE];
|
|
in.rdbuf()->pubsetbuf(inBuffer, FILE_BUFFER_SIZE);
|
|
char header[100];
|
|
in.seekg(0);
|
|
in.read(header,100);
|
|
|
|
uint32_t partition = 0;
|
|
ofstream* out = 0;
|
|
ofstream* outx = 0;
|
|
|
|
string base = f.substr(0,f.length()-4);
|
|
char* outBuffer1 = new char[FILE_BUFFER_SIZE];
|
|
char* outBuffer2 = new char[FILE_BUFFER_SIZE];
|
|
|
|
uint32_t recno = 0;
|
|
bool firstRecord=true;
|
|
|
|
while(in.good() && (in.tellg() < fileSize)){
|
|
if(!out){ // start a new partition
|
|
out = new ofstream( (base + "_"
|
|
+ stringutils::int2str(partition)
|
|
+ ".shp").c_str(),
|
|
ios::binary);
|
|
out->rdbuf()->pubsetbuf(outBuffer1, FILE_BUFFER_SIZE);
|
|
out->write(header,100);
|
|
recno = 0;
|
|
if(cx){
|
|
outx = new ofstream( (base +"_"
|
|
+ stringutils::int2str(partition)
|
|
+ ".shx").c_str(),
|
|
ios::binary);
|
|
outx->rdbuf()->pubsetbuf(outBuffer2, FILE_BUFFER_SIZE);
|
|
outx->write(header,100);
|
|
}
|
|
partition++;
|
|
firstRecord = true;
|
|
}
|
|
// copy records until end of file or maximum number of records
|
|
// for a single partition is reached
|
|
double minX=0;
|
|
double minY=0;
|
|
double maxX=0;
|
|
double maxY=0;
|
|
while( in.good() && (recno < (uint32_t) maxnorec)
|
|
&& (in.tellg()<fileSize)){
|
|
in.seekg(4,ios::cur); // ignore original record number
|
|
uint32_t cl = readBigInt32(in);
|
|
char* record = new char[2*cl];
|
|
in.read(record, 2*cl);
|
|
if(in.good()){
|
|
updateBox(type, record, cl, minX,minY,maxX,maxY,firstRecord);
|
|
uint32_t offset = out->tellp()/2;
|
|
recno++;
|
|
WinUnix::writeBigEndian(*out, recno);
|
|
WinUnix::writeBigEndian(*out,cl);
|
|
out->write(record, 2*cl);
|
|
if(outx){
|
|
WinUnix::writeBigEndian(*outx, offset);
|
|
WinUnix::writeBigEndian(*outx, cl);
|
|
}
|
|
}
|
|
delete[] record;
|
|
}
|
|
uint32_t fs = out->tellp()/2;
|
|
if(fs>50) { // more than header size
|
|
// update file length and bounding box in header
|
|
out->seekp(24, ios::beg);
|
|
WinUnix::writeBigEndian(*out,fs);
|
|
out->seekp(36,ios::beg);
|
|
WinUnix::writeLittle64(*out,minX);
|
|
WinUnix::writeLittle64(*out,minY);
|
|
WinUnix::writeLittle64(*out,maxX);
|
|
WinUnix::writeLittle64(*out,maxY);
|
|
if(outx){
|
|
fs= outx->tellp()/2;
|
|
outx->seekp(24, ios::beg);
|
|
WinUnix::writeBigEndian(*outx,fs);
|
|
outx->seekp(36,ios::beg);
|
|
WinUnix::writeLittle64(*outx,minX);
|
|
WinUnix::writeLittle64(*outx,minY);
|
|
WinUnix::writeLittle64(*outx,maxX);
|
|
WinUnix::writeLittle64(*outx,maxY);
|
|
}
|
|
}
|
|
out->close();
|
|
delete out;
|
|
out = 0;
|
|
if(outx){
|
|
outx->close();
|
|
delete outx;
|
|
outx=0;
|
|
}
|
|
}
|
|
delete[] inBuffer;
|
|
delete[] outBuffer1;
|
|
delete[] outBuffer2;
|
|
res->Set(true,partition);
|
|
return 0;
|
|
}
|
|
|
|
|
|
ValueMapping splitShpVM[] = {
|
|
splitShpVMT<CcString>,
|
|
splitShpVMT<FText>
|
|
};
|
|
|
|
int splitShpSelect(ListExpr args){
|
|
return CcString::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
OperatorSpec splitShpSpec(
|
|
"{string, text} x int [ x bool] -> int ",
|
|
"spitShp(source, maxRecNo, createIndex) ",
|
|
"Split a single shape file into a serie of "
|
|
"shape files each having atmost maxRecNo entries. "
|
|
"Of course, the last partition may have less entries."
|
|
"The partition names come from the original name with appended "
|
|
"_<partition_numer>, where numbering starts with zero. "
|
|
"If the optional boolean argument is given and true, "
|
|
"for each created partition a corresponding shx file is created too.",
|
|
"query splitShp('buildings.shp', 600000, TRUE)"
|
|
);
|
|
|
|
Operator splitShpOp(
|
|
"splitShp",
|
|
splitShpSpec.getStr(),
|
|
2,
|
|
splitShpVM,
|
|
splitShpSelect,
|
|
splitShpTM
|
|
);
|
|
|
|
|
|
/*
|
|
24.29 Operator ~splitDB3~
|
|
|
|
This operator splits a dbase-III file into a series of dbase-III
|
|
files having at most a certain number of elements stored.
|
|
|
|
*/
|
|
ListExpr splitDB3TM(ListExpr args){
|
|
string err = "{string,text} x int [x bool] expected";
|
|
if(!nl->HasLength(args,2) ){
|
|
return listutils::typeError(err + " (wrong number of arguments)");
|
|
}
|
|
if( !CcString::checkType(nl->First(args))
|
|
&& !FText::checkType(nl->First(args))){
|
|
return listutils::typeError(err + " (first arg is neither a "
|
|
"string nor a text)");
|
|
}
|
|
if(!CcInt::checkType(nl->Second(args))){
|
|
return listutils::typeError(err + " (second arg in not an int)");
|
|
}
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
|
|
|
|
template<class T>
|
|
int splitDB3VMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = (CcInt*) result.addr;
|
|
T* fn = (T*) args[0].addr;
|
|
CcInt* rn = (CcInt*) args[1].addr;
|
|
if(!fn->IsDefined() || !rn->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
string f = fn->GetValue();
|
|
int r = rn->GetValue();
|
|
if(r<1){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
if(!stringutils::endsWith(f,".dbf")){
|
|
cerr << "file name does not end with .dbf" << endl;
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
ifstream in(f.c_str(), ios::binary);
|
|
if(!in.good()){
|
|
cerr << "could not open file " << f << endl;
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
unsigned char version;
|
|
in.read((char*)(&version),1);
|
|
|
|
if(!in.good() || ((version!=0x02) && (version!=0x03) && (version!=0x83))){
|
|
cerr << "file " << f << " is not a supported d-base file" << endl;
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
if(version==0x83){
|
|
cerr << "Db-III file with Memofields are not supported yet" << endl;
|
|
res->Set(true,0);
|
|
return 0;
|
|
}
|
|
char* inBuffer = new char[FILE_BUFFER_SIZE];
|
|
in.rdbuf()->pubsetbuf(inBuffer, FILE_BUFFER_SIZE);
|
|
in.seekg(4);
|
|
uint32_t noRecords = readLittleInt32(in);
|
|
uint16_t headerLength = readLittleInt16(in);
|
|
uint16_t recordLength = readLittleInt16(in);
|
|
char* header = new char[headerLength];
|
|
in.seekg(0);
|
|
in.read(header, headerLength);
|
|
if(!in.good()){
|
|
cerr << "error in reading header from file " << f << endl;
|
|
res->Set(true,0);
|
|
delete[] header;
|
|
delete[] inBuffer;
|
|
return 0;
|
|
}
|
|
|
|
char* outBuffer = new char[FILE_BUFFER_SIZE];
|
|
|
|
uint32_t partition = 0;
|
|
ofstream* out = 0;
|
|
string base = f.substr(0,f.length()-4);
|
|
size_t bytes2copy;
|
|
|
|
uint32_t bs = 16*1024;
|
|
char buf[bs];
|
|
|
|
|
|
while(in.good() && !in.eof() && noRecords>0){
|
|
out = new ofstream(( base + "_"
|
|
+ stringutils::int2str(partition)
|
|
+ ".dbf").c_str(),
|
|
ios::binary);
|
|
out->rdbuf()->pubsetbuf(outBuffer, FILE_BUFFER_SIZE);
|
|
uint32_t records2Copy = (uint32_t)r < noRecords?r: noRecords;
|
|
bytes2copy = records2Copy*recordLength;
|
|
noRecords -= records2Copy;
|
|
// change noRecords within header
|
|
if(!WinUnix::isLittleEndian()){
|
|
WinUnix::convertEndian(records2Copy);
|
|
}
|
|
memcpy(header+4,&records2Copy,4);
|
|
out->write(header, headerLength);
|
|
partition++;
|
|
while(bytes2copy>0){
|
|
size_t b = bs<bytes2copy?bs:bytes2copy;
|
|
in.read(buf,b);
|
|
out->write(buf,b);
|
|
bytes2copy -= b;
|
|
}
|
|
unsigned char dataEndMarker=0x1a;
|
|
out->write((char*)(&dataEndMarker),1);
|
|
out->close();
|
|
delete out;
|
|
}
|
|
delete[] inBuffer;
|
|
delete[] outBuffer;
|
|
delete[] header;
|
|
res->Set(true,partition);
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping splitDB3VM[] = {
|
|
splitDB3VMT<CcString>,
|
|
splitDB3VMT<FText>
|
|
};
|
|
|
|
int splitDB3Select(ListExpr args){
|
|
return CcString::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
OperatorSpec splitDB3Spec(
|
|
"{string, text} x int -> int",
|
|
"splitDB3(fileName, noRecords) ",
|
|
"Splits a d-base III file into a serie of such files "
|
|
"echa containing at most noRecords entries (the last file "
|
|
"may have less entries. "
|
|
"The filenames are taken from the input file name, extended by "
|
|
"an underscore and the partition number. "
|
|
"The operator returns the number of created partitions.",
|
|
"query splitDB3('ten.dbf',2)"
|
|
);
|
|
|
|
Operator splitDB3Op(
|
|
"splitDB3",
|
|
splitDB3Spec.getStr(),
|
|
2,
|
|
splitDB3VM,
|
|
splitDB3Select,
|
|
splitDB3TM
|
|
|
|
);
|
|
|
|
/*
|
|
24.99 Operator ~aisimport~
|
|
|
|
24.99.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr importaisTM(ListExpr args){
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError("two Arguments expected");
|
|
}
|
|
ListExpr arg1 = nl->First(args);
|
|
ListExpr arg2 = nl->Second(args);
|
|
|
|
if(!nl->HasLength(arg1,2) || !nl->HasLength(arg2,2)){
|
|
return listutils::typeError("internal error");
|
|
}
|
|
arg1 = nl->First(arg1); // extract type
|
|
|
|
if(!CcString::checkType(arg1) && !FText::checkType(arg1)){
|
|
return listutils::typeError("expected string or text as first argument");
|
|
}
|
|
if(!CcString::checkType(nl->First(arg2))){
|
|
return listutils::typeError("expected string as second argument");
|
|
}
|
|
|
|
arg2 = nl->Second(arg2);
|
|
if(nl->AtomType(arg2)!=StringType){
|
|
return listutils::typeError("second argument must be a constant string");
|
|
}
|
|
string prefix = nl->StringValue(arg2);
|
|
if(!stringutils::isIdent(prefix)){
|
|
return listutils::typeError("second argument is not an valid identifier");
|
|
}
|
|
|
|
SecondoCatalog* ctlg = SecondoSystem::GetCatalog();
|
|
// check for the 27 message types whether there is already a relation
|
|
for(int mt = 1 ; mt <28; mt++){
|
|
string rname = prefix + "_" + stringutils::int2str(mt);
|
|
if(ctlg->IsObjectName(rname)){
|
|
return listutils::typeError(rname + " is already present");
|
|
}
|
|
}
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
|
|
|
|
/*
|
|
24.99.2 Info class
|
|
|
|
*/
|
|
struct ClusterInfo{
|
|
|
|
|
|
bool operator<(const ClusterInfo& ci) const{
|
|
if(minTime < ci.minTime) return true;
|
|
if(minTime > ci.minTime) return false;
|
|
if(maxTime < ci.maxTime) return true;
|
|
if(maxTime > ci.maxTime) return false;
|
|
return count < ci.count;
|
|
}
|
|
|
|
|
|
datetime::DateTime minTime;
|
|
datetime::DateTime maxTime;
|
|
size_t count;
|
|
|
|
ostream& print(ostream& out)const{
|
|
out << "[ " << minTime << " , " << maxTime << "] : " << count;
|
|
return out;
|
|
}
|
|
|
|
bool contains(const datetime::DateTime& time) const{
|
|
return (time>=minTime) && (time<=maxTime);
|
|
}
|
|
|
|
|
|
ClusterInfo(datetime::DateTime& dt): minTime(dt), maxTime(dt), count(1){}
|
|
|
|
void add(const ClusterInfo& cl){
|
|
if(minTime > cl.minTime){
|
|
minTime = cl.minTime;
|
|
}
|
|
if(maxTime < cl.maxTime){
|
|
maxTime = cl.maxTime;
|
|
}
|
|
count += cl.count;
|
|
}
|
|
|
|
void add(const datetime::DateTime& dt){
|
|
count++;
|
|
if(dt < minTime){
|
|
minTime = dt;
|
|
return;
|
|
}
|
|
if(dt > maxTime) {
|
|
maxTime = dt;
|
|
}
|
|
}
|
|
|
|
datetime::DateTime distance(const datetime::DateTime& dt)const{
|
|
if(dt<minTime){
|
|
return minTime - dt;
|
|
}
|
|
if(dt > maxTime){
|
|
return dt - maxTime;
|
|
}
|
|
datetime::DateTime r(datetime::durationtype, 0);
|
|
return r;
|
|
}
|
|
|
|
datetime::DateTime distance(const ClusterInfo& ci) const{
|
|
if(minTime > ci.maxTime){
|
|
return minTime - ci.maxTime;
|
|
}
|
|
if(maxTime < ci.minTime){
|
|
return ci.minTime - maxTime;
|
|
}
|
|
return datetime::DateTime(datetime::durationtype,0);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
ostream& operator<<(ostream& o, const ClusterInfo& ci){
|
|
return ci.print(o);
|
|
}
|
|
|
|
|
|
struct Type4Info{
|
|
|
|
Type4Info():mmsi(0),count(0),failed(0),clusters(){
|
|
uint64_t t = 1000*60*60;
|
|
threshold =datetime::DateTime(datetime::durationtype,t);
|
|
}
|
|
|
|
Type4Info(aisdecode::Message4* msg): mmsi(msg->mmsi), count(0), failed(0),
|
|
clusters(){
|
|
uint64_t t = 1000*60*60;
|
|
threshold =datetime::DateTime(datetime::durationtype,t);
|
|
add(msg);
|
|
}
|
|
|
|
Type4Info(const Type4Info& ti): mmsi(ti.mmsi), count(ti.count),
|
|
failed(ti.failed), clusters(ti.clusters){
|
|
uint64_t t = 1000*60*60;
|
|
threshold =datetime::DateTime(datetime::durationtype,t);
|
|
}
|
|
|
|
bool contains(const datetime::DateTime& time){
|
|
for(size_t i=0;i<clusters.size();i++){
|
|
if(clusters[i].contains(time)){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
Type4Info& operator=(const Type4Info& ti){
|
|
mmsi = ti.mmsi;
|
|
count = ti.count;
|
|
failed = ti.failed;
|
|
clusters = ti.clusters;
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
void add(aisdecode::Message4* msg) {
|
|
assert(msg->mmsi==this->mmsi);
|
|
count++;
|
|
if(msg->year<1 || msg->month<1 || msg->day<1){
|
|
failed++;
|
|
return;
|
|
}
|
|
if(msg->year>9999 || msg->month>12 || msg->day>31){
|
|
failed++;
|
|
return;
|
|
}
|
|
if(msg->hour > 23 || msg->minute>59 || msg->second>59){
|
|
failed++;
|
|
return;
|
|
}
|
|
|
|
datetime::DateTime time(datetime::instanttype);
|
|
if(!time.IsValid(msg->year, msg->month, msg->day)){
|
|
failed++;
|
|
return;
|
|
}
|
|
time.Set(msg->year, msg->month, msg->day, msg->hour, msg->minute,
|
|
msg->second);
|
|
|
|
insert(time);
|
|
}
|
|
|
|
/*
|
|
If the is a cluster containing time, the count of the cluster is increased..
|
|
otherwise the nearest cluster is determined. if the distance is smaller than a
|
|
threshold, the clustr
|
|
|
|
*/
|
|
void insert(datetime::DateTime& time){
|
|
if(clusters.empty()){
|
|
ClusterInfo ci(time);
|
|
clusters.push_back(ci);
|
|
return;
|
|
}
|
|
int minIndex = 0;
|
|
datetime::DateTime minDist = clusters[0].distance(time);
|
|
for(size_t i=1;i<clusters.size();i++){
|
|
datetime::DateTime dist = clusters[i].distance(time);
|
|
if(dist < minDist){
|
|
minIndex = i;
|
|
minDist = dist;
|
|
}
|
|
}
|
|
// threshold 1 hour
|
|
if(minDist < threshold){
|
|
clusters[minIndex].add(time);
|
|
} else {
|
|
clusters.push_back(ClusterInfo(time));
|
|
}
|
|
}
|
|
|
|
ostream& print(ostream& out)const{
|
|
out << "---------" << endl;
|
|
out << "MMSI : " << mmsi << endl;
|
|
out << "#messages : " << count << endl;
|
|
out << "invalid messages : " << failed << endl;
|
|
out << "clusters" << endl;
|
|
for(size_t i=0;i<clusters.size();i++){
|
|
out << " " << clusters[i] << endl;
|
|
}
|
|
out << "---------" << endl;
|
|
return out;
|
|
};
|
|
|
|
void mergeCluster(){
|
|
if(clusters.size() < 2) return;
|
|
sort(clusters.begin(),clusters.end());
|
|
vector<ClusterInfo> merged;
|
|
merged.push_back(clusters[0]);
|
|
int pos = 0;
|
|
for(size_t i=1;i<clusters.size();i++){
|
|
datetime::DateTime dist = merged[pos].distance(clusters[i]);
|
|
if(dist<threshold){
|
|
merged[pos].add(clusters[i]);
|
|
} else {
|
|
merged.push_back(clusters[i]);
|
|
pos++;
|
|
}
|
|
}
|
|
swap(clusters, merged);
|
|
}
|
|
|
|
size_t largestClusterSize() const{
|
|
size_t res = 0;
|
|
for(size_t i=0;i<clusters.size();i++){
|
|
if(clusters[i].count > res){
|
|
res = clusters[i].count;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
void add(const Type4Info& t4i){
|
|
count += t4i.count;
|
|
failed += t4i.failed;
|
|
for(size_t i=0;i<t4i.clusters.size();i++){
|
|
clusters.push_back(t4i.clusters[i]);
|
|
}
|
|
}
|
|
|
|
|
|
void purge(){
|
|
double minClusterSize = largestClusterSize()* 0.01;
|
|
vector<ClusterInfo> purged;
|
|
for(size_t i=0;i<clusters.size();i++){
|
|
if(clusters[i].count >= minClusterSize){
|
|
purged.push_back(clusters[i]);
|
|
}
|
|
}
|
|
swap(purged,clusters);
|
|
}
|
|
|
|
void clear(){
|
|
mmsi = 0;
|
|
count = 0;
|
|
failed = 0;
|
|
clusters.clear();
|
|
}
|
|
|
|
int mmsi;
|
|
size_t count;
|
|
size_t failed;
|
|
vector<ClusterInfo> clusters;
|
|
datetime::DateTime threshold;
|
|
|
|
};
|
|
|
|
|
|
ostream& operator<<(ostream& o, const Type4Info& ti){
|
|
return ti.print(o);
|
|
}
|
|
|
|
|
|
class aisimportInfo{
|
|
|
|
public:
|
|
aisimportInfo(const string& _filename,
|
|
const string& _prefix): filename(_filename),
|
|
prefix(_prefix),
|
|
time(datetime::instanttype,0)
|
|
{
|
|
cout << "decode file " << _filename << endl;
|
|
time.SetDefined(false);
|
|
for(int i=0;i<27;i++){
|
|
relations.push_back(0);
|
|
tupleTypes.push_back(0);
|
|
}
|
|
ctlg = SecondoSystem::GetCatalog();
|
|
noUsedReason=0;
|
|
}
|
|
|
|
~aisimportInfo(){
|
|
for(int i=0;i<27;i++){
|
|
if(relations[i]){
|
|
// relation deletion is handled by catalog
|
|
tupleTypes[i]->DeleteIfAllowed();
|
|
}
|
|
}
|
|
}
|
|
|
|
int operator()(){
|
|
|
|
count =0;
|
|
aisdecode::MessageBase* msg;
|
|
aisdecode::aisdecoder* dec = new aisdecode::aisdecoder(filename);
|
|
// proprocessing of type 4 messages
|
|
map<int,Type4Info> mmsicount;
|
|
map<int,Type4Info>::iterator it;
|
|
while( (msg = dec->getNextMessage(4))){
|
|
count++;
|
|
aisdecode::Message4* msg4 = (aisdecode::Message4*) msg;
|
|
it = mmsicount.find(msg4->mmsi);
|
|
if(it==mmsicount.end()){
|
|
mmsicount[msg4->mmsi] = Type4Info(msg4);
|
|
} else {
|
|
it->second.add(msg4);
|
|
}
|
|
delete msg;
|
|
}
|
|
// connect cluster for each mmsi
|
|
for(it=mmsicount.begin(); it!=mmsicount.end();it++){
|
|
it->second.mergeCluster();
|
|
}
|
|
|
|
// build a set of forbidden mmsi
|
|
// an mmsi is forbidden if
|
|
// number of messages < 2
|
|
// number of messages < 10% than the average per mmsi
|
|
// number of invalid messages > 10% for this mmsi
|
|
// the largest cluster has less than 5% messages in average
|
|
forbidden.clear();
|
|
|
|
double average = count *1.0 / mmsicount.size();
|
|
double min = average * 0.1;
|
|
if(min < 2) min = 2.5;
|
|
double minCluster = average * 0.05;
|
|
|
|
for(it=mmsicount.begin(); it!=mmsicount.end();it++){
|
|
if(it->second.count < average){
|
|
forbidden.insert(it->first);
|
|
} else {
|
|
if( (it->second.failed*1.0 / it->second.count) > 0.1){
|
|
forbidden.insert(it->first);
|
|
} else {
|
|
if(it->second.largestClusterSize() < minCluster){
|
|
forbidden.insert(it->first);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// for the remaining mmsi
|
|
// union all clusters
|
|
// remove clusters with too less entries
|
|
complete.clear();
|
|
for(it=mmsicount.begin(); it!=mmsicount.end();it++){
|
|
if(forbidden.find(it->first)==forbidden.end()){
|
|
complete.add(it->second);
|
|
}
|
|
}
|
|
complete.mergeCluster();
|
|
complete.purge();
|
|
|
|
cout << "ignore type 4 messages of " << forbidden.size()
|
|
<< " mmsis" << endl;
|
|
cout << "allow only time in the intervals " << complete << endl;
|
|
|
|
|
|
count =0;
|
|
delete dec;
|
|
|
|
posMessages.clear();
|
|
|
|
dec = new aisdecode::aisdecoder(filename);
|
|
while( (msg = dec->getNextMessage())){
|
|
count++;
|
|
processMessage1(msg);
|
|
}
|
|
delete dec;
|
|
if(time.IsDefined()){
|
|
processPosMessages(time,time);
|
|
} else {
|
|
assert(posMessages.empty());
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
string filename;
|
|
string prefix;
|
|
Instant time;
|
|
vector<Relation*> relations;
|
|
vector<TupleType*> tupleTypes;
|
|
SecondoCatalog* ctlg;
|
|
size_t count; // total number of messages
|
|
map<int, datetime::DateTime> lastTimes;
|
|
set<int> forbidden; // excluded mmsi for type 4 messages
|
|
Type4Info complete;
|
|
int noUsedReason; // integer coded reason why a type 4 message has
|
|
// been ignored
|
|
vector<aisdecode::MessageBase*> posMessages;
|
|
|
|
|
|
|
|
void createRelationForType(int type, ListExpr attrList){
|
|
ListExpr tupleList = nl->TwoElemList(listutils::basicSymbol<Tuple>(),
|
|
attrList);
|
|
ListExpr relType = nl->TwoElemList(listutils::basicSymbol<Relation>(),
|
|
tupleList);
|
|
TupleType* tt = new TupleType(ctlg->NumericType(nl->Second(relType)));
|
|
tupleTypes[type-1] = tt;
|
|
relations[type-1] = new Relation(tt);
|
|
Word relWord;
|
|
relWord.setAddr(relations[type-1]);
|
|
string name = prefix+"_" + stringutils::int2str(type);
|
|
if(!ctlg->InsertObject( name, "",
|
|
relType, relWord,true)){
|
|
cerr << "prolem in inserting relation " << name << "_" << endl;
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Message types 1-3
|
|
|
|
*/
|
|
void writeMessage(aisdecode::Message1_3* msg){
|
|
if(relations[0] == 0){ // relation not present
|
|
ListExpr attrList = nl->OneElemList(
|
|
nl->TwoElemList(nl->SymbolAtom("Type"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
ListExpr last = attrList;
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Repeat"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Status"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Rot"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("SOG"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Accuracy"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Longitude"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Latitude"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("COG"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Heading"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Time"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Maneuver"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Raim"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Radio"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("TimeStamp"),
|
|
listutils::basicSymbol<Instant>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MessageNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
createRelationForType(1,attrList);
|
|
}
|
|
Tuple* tuple = new Tuple(tupleTypes[0]);
|
|
tuple->PutAttribute(0, new CcInt(true,msg->messageType));
|
|
tuple->PutAttribute(1, new CcInt(true,msg->repeatIndicator));
|
|
tuple->PutAttribute(2, new CcInt(true,msg->mmsi));
|
|
tuple->PutAttribute(3, new CcInt(true,msg->status));
|
|
tuple->PutAttribute(4, new CcInt(true,msg->rot));
|
|
tuple->PutAttribute(5, new CcInt(true,msg->sog));
|
|
tuple->PutAttribute(6, new CcInt(true,msg->accuracy));
|
|
tuple->PutAttribute(7, new CcReal(true,msg->longitude));
|
|
tuple->PutAttribute(8, new CcReal(true,msg->latitude));
|
|
tuple->PutAttribute(9, new CcInt(true,msg->cog));
|
|
tuple->PutAttribute(10, new CcInt(true,msg->heading));
|
|
tuple->PutAttribute(11, new CcInt(true,msg->second));
|
|
tuple->PutAttribute(12, new CcInt(true,msg->maneuver));
|
|
tuple->PutAttribute(13, new CcInt(true,msg->raim));
|
|
tuple->PutAttribute(14, new CcInt(true,msg->rstatus));
|
|
tuple->PutAttribute(15, new datetime::DateTime(time));
|
|
tuple->PutAttribute(16, new CcInt(true,count));
|
|
relations[0]->AppendTuple(tuple);
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
|
|
/*
|
|
Message type 4
|
|
|
|
*/
|
|
void writeMessage(aisdecode::Message4* msg){
|
|
if(relations[3]==0){
|
|
ListExpr attrList = nl->OneElemList(
|
|
nl->TwoElemList(nl->SymbolAtom("Type"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
ListExpr last = attrList;
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Repeat"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Year"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Month"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Day"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Hour"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Minute"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Second"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Fix"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Longitude"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Latitude"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("EPFD"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Raim"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("SotDMA"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("TimeStamp"),
|
|
listutils::basicSymbol<datetime::DateTime>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MessageNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("NoUsedReason"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
createRelationForType(4,attrList);
|
|
}
|
|
Tuple* tuple = new Tuple(tupleTypes[3]);
|
|
tuple->PutAttribute(0, new CcInt(true,msg->type));
|
|
tuple->PutAttribute(1, new CcInt(true,msg->repeat));
|
|
tuple->PutAttribute(2, new CcInt(true,msg->mmsi));
|
|
tuple->PutAttribute(3, new CcInt(true,msg->year));
|
|
tuple->PutAttribute(4, new CcInt(true,msg->month));
|
|
tuple->PutAttribute(5, new CcInt(true,msg->day));
|
|
tuple->PutAttribute(6, new CcInt(true,msg->hour));
|
|
tuple->PutAttribute(7, new CcInt(true,msg->minute));
|
|
tuple->PutAttribute(8, new CcInt(true,msg->second));
|
|
tuple->PutAttribute(9, new CcInt(true,msg->fix));
|
|
tuple->PutAttribute(10, new CcReal(true,msg->longitude));
|
|
tuple->PutAttribute(11, new CcReal(true,msg->latitude));
|
|
tuple->PutAttribute(12, new CcInt(true,msg->epfd));
|
|
tuple->PutAttribute(13, new CcInt(true,msg->raim));
|
|
tuple->PutAttribute(14, new CcInt(true,msg->sotdma));
|
|
tuple->PutAttribute(15, new Instant(time));
|
|
tuple->PutAttribute(16, new CcInt(true,count));
|
|
tuple->PutAttribute(17, new CcInt(true,noUsedReason));
|
|
relations[3]->AppendTuple(tuple);
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
|
|
/*
|
|
Message type 5
|
|
|
|
*/
|
|
void writeMessage(aisdecode::Message5* msg){
|
|
if(relations[4] == 0){
|
|
ListExpr attrList = nl->OneElemList(
|
|
nl->TwoElemList(nl->SymbolAtom("Type"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
ListExpr last = attrList;
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Repeat"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("AisVersion"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("IMO"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("CallSign"),
|
|
listutils::basicSymbol<CcString>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("VesselName"),
|
|
listutils::basicSymbol<CcString>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("ShipType"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DimToBow"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DimToStern"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DimToPort"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(
|
|
nl->SymbolAtom("DimToStarboard"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Epfd"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("ETA_Month"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("ETA_Day"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("ETA_HOUR"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("ETA_Minute"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Draught"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Destination"),
|
|
listutils::basicSymbol<CcString>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DTE"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("TimeStamp"),
|
|
listutils::basicSymbol<Instant>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MessageNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
|
|
createRelationForType(5,attrList);
|
|
}
|
|
Tuple* tuple = new Tuple(tupleTypes[4]);
|
|
tuple->PutAttribute(0, new CcInt(true,msg->type));
|
|
tuple->PutAttribute(1, new CcInt(true,msg->repeat));
|
|
tuple->PutAttribute(2, new CcInt(true,msg->mmsi));
|
|
tuple->PutAttribute(3, new CcInt(true,msg->ais_version));
|
|
tuple->PutAttribute(4, new CcInt(true,msg->imo));
|
|
tuple->PutAttribute(5, new CcString(true,msg->callSign));
|
|
tuple->PutAttribute(6, new CcString(true,msg->vesselName));
|
|
tuple->PutAttribute(7, new CcInt(true,msg->shipType));
|
|
tuple->PutAttribute(8, new CcInt(true,msg->dimToBow));
|
|
tuple->PutAttribute(9, new CcInt(true,msg->dimToStern));
|
|
tuple->PutAttribute(10, new CcInt(true,msg->dimToPort));
|
|
tuple->PutAttribute(11, new CcInt(true,msg->dimToStarboard));
|
|
tuple->PutAttribute(12, new CcInt(true,msg->epfd));
|
|
tuple->PutAttribute(13, new CcInt(true,msg->month));
|
|
tuple->PutAttribute(14, new CcInt(true,msg->day));
|
|
tuple->PutAttribute(15, new CcInt(true,msg->hour));
|
|
tuple->PutAttribute(16, new CcInt(true,msg->minute));
|
|
tuple->PutAttribute(17, new CcInt(true,msg->draught));
|
|
tuple->PutAttribute(18, new CcString(true,msg->destination));
|
|
tuple->PutAttribute(19, new CcInt(true,msg->dte));
|
|
tuple->PutAttribute(20, new Instant(time));
|
|
tuple->PutAttribute(21, new CcInt(true,count));
|
|
relations[4]->AppendTuple(tuple);
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
|
|
|
|
/*
|
|
Message type 9
|
|
|
|
*/
|
|
void writeMessage(aisdecode::Message9* msg){
|
|
if(relations[8]==0){
|
|
ListExpr attrList = nl->OneElemList(
|
|
nl->TwoElemList(nl->SymbolAtom("Type"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
ListExpr last = attrList;
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Repeat"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Alt"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("SOG"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Accuracy"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Longitude"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Latitude"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("COG"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Second"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DTE"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Assigned"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Raim"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Radio"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("TimeStamp"),
|
|
listutils::basicSymbol<Instant>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MessageNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
|
|
createRelationForType(9,attrList);
|
|
}
|
|
Tuple* tuple = new Tuple(tupleTypes[8]);
|
|
tuple->PutAttribute(0, new CcInt(true,msg->type));
|
|
tuple->PutAttribute(1, new CcInt(true,msg->repeat));
|
|
tuple->PutAttribute(2, new CcInt(true,msg->mmsi));
|
|
tuple->PutAttribute(3, new CcInt(true,msg->alt));
|
|
tuple->PutAttribute(4, new CcInt(true,msg->sog));
|
|
tuple->PutAttribute(5, new CcInt(true,msg->accuracy));
|
|
tuple->PutAttribute(6, new CcReal(true,msg->longitude));
|
|
tuple->PutAttribute(7, new CcReal(true,msg->latitude));
|
|
tuple->PutAttribute(8, new CcInt(true,msg->cog));
|
|
tuple->PutAttribute(9, new CcInt(true,msg->second));
|
|
tuple->PutAttribute(10, new CcInt(true,msg->dte));
|
|
tuple->PutAttribute(11, new CcInt(true,msg->assigned));
|
|
tuple->PutAttribute(12, new CcInt(true,msg->raim));
|
|
tuple->PutAttribute(13, new CcInt(true,msg->radio));
|
|
tuple->PutAttribute(14, new Instant(time));
|
|
tuple->PutAttribute(15, new CcInt(true,count));
|
|
relations[8]->AppendTuple(tuple);
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
|
|
/*
|
|
Message type 12
|
|
|
|
*/
|
|
void writeMessage(aisdecode::Message12* msg){
|
|
if(relations[11]==0){
|
|
ListExpr attrList = nl->OneElemList(
|
|
nl->TwoElemList(nl->SymbolAtom("Type"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
ListExpr last = attrList;
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Repeat"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("SeqNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Dest_MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Retransmit"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Text"),
|
|
listutils::basicSymbol<FText>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("TimeStamp"),
|
|
listutils::basicSymbol<Instant>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MessageNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
createRelationForType(12,attrList);
|
|
}
|
|
Tuple* tuple = new Tuple(tupleTypes[11]);
|
|
tuple->PutAttribute(0, new CcInt(true,msg->type));
|
|
tuple->PutAttribute(1, new CcInt(true,msg->repeat));
|
|
tuple->PutAttribute(2, new CcInt(true,msg->source_mmsi));
|
|
tuple->PutAttribute(3, new CcInt(true,msg->sequence_number));
|
|
tuple->PutAttribute(4, new CcInt(true,msg->dest_mmsi));
|
|
tuple->PutAttribute(5, new CcInt(true,msg->retransmit));
|
|
tuple->PutAttribute(6, new FText(true,msg->text));
|
|
tuple->PutAttribute(7, new Instant(time));
|
|
tuple->PutAttribute(8, new CcInt(true,count));
|
|
relations[11]->AppendTuple(tuple);
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
|
|
/*
|
|
Message type 14
|
|
|
|
*/
|
|
void writeMessage(aisdecode::Message14* msg){
|
|
if(relations[13]==0){
|
|
ListExpr attrList = nl->OneElemList(
|
|
nl->TwoElemList(nl->SymbolAtom("Type"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
ListExpr last = attrList;
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Repeat"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Text"),
|
|
listutils::basicSymbol<FText>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("TimeStamp"),
|
|
listutils::basicSymbol<Instant>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MessageNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
createRelationForType(14,attrList);
|
|
}
|
|
Tuple* tuple = new Tuple(tupleTypes[13]);
|
|
tuple->PutAttribute(0, new CcInt(true,msg->type));
|
|
tuple->PutAttribute(1, new CcInt(true,msg->repeat));
|
|
tuple->PutAttribute(2, new CcInt(true,msg->mmsi));
|
|
tuple->PutAttribute(3, new FText(true,msg->text));
|
|
tuple->PutAttribute(4, new Instant(time));
|
|
tuple->PutAttribute(5, new CcInt(true,count));
|
|
|
|
relations[13]->AppendTuple(tuple);
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
|
|
|
|
/*
|
|
Message type 18
|
|
|
|
*/
|
|
void writeMessage(aisdecode::Message18* msg){
|
|
if(relations[17]==0){
|
|
ListExpr attrList = nl->OneElemList(
|
|
nl->TwoElemList(nl->SymbolAtom("Type"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
ListExpr last = attrList;
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Repeat"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("SOG"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Accuracy"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Longitude"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Latitude"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("COG"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Heading"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Second"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("CS"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Display"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DSC"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Band"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Msg22"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Assigned"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Raim"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Radio"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("TimeStamp"),
|
|
listutils::basicSymbol<Instant>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MessageNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
|
|
createRelationForType(18,attrList);
|
|
}
|
|
Tuple* tuple = new Tuple(tupleTypes[17]);
|
|
tuple->PutAttribute(0, new CcInt(true,msg->type));
|
|
tuple->PutAttribute(1, new CcInt(true,msg->repeat));
|
|
tuple->PutAttribute(2, new CcInt(true,msg->mmsi));
|
|
tuple->PutAttribute(3, new CcInt(true,msg->sog));
|
|
tuple->PutAttribute(4, new CcInt(true,msg->accuracy));
|
|
tuple->PutAttribute(5, new CcReal(true,msg->longitude));
|
|
tuple->PutAttribute(6, new CcReal(true,msg->latitude));
|
|
tuple->PutAttribute(7, new CcInt(true,msg->cog));
|
|
tuple->PutAttribute(8, new CcInt(true,msg->heading));
|
|
tuple->PutAttribute(9, new CcInt(true,msg->second));
|
|
tuple->PutAttribute(10, new CcInt(true,msg->cs));
|
|
tuple->PutAttribute(11, new CcInt(true,msg->display));
|
|
tuple->PutAttribute(12, new CcInt(true,msg->dsc));
|
|
tuple->PutAttribute(13, new CcInt(true,msg->band));
|
|
tuple->PutAttribute(14, new CcInt(true,msg->msg22));
|
|
tuple->PutAttribute(15, new CcInt(true,msg->assigned));
|
|
tuple->PutAttribute(16, new CcInt(true,msg->raim));
|
|
tuple->PutAttribute(17, new CcInt(true,msg->radio));
|
|
tuple->PutAttribute(18, new Instant(time));
|
|
tuple->PutAttribute(19, new CcInt(true,count));
|
|
relations[17]->AppendTuple(tuple);
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
|
|
|
|
/*
|
|
Message type 19
|
|
|
|
*/
|
|
void writeMessage(aisdecode::Message19* msg){
|
|
if(relations[18]==0){
|
|
ListExpr attrList = nl->OneElemList(
|
|
nl->TwoElemList(nl->SymbolAtom("Type"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
ListExpr last = attrList;
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Repeat"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("SOG"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Accuracy"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Longitude"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Latitude"),
|
|
listutils::basicSymbol<CcReal>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("COG"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Heading"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Second"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Name"),
|
|
listutils::basicSymbol<CcString>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("ShipType"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DimToBow"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DimToStern"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DimToPort"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(
|
|
nl->SymbolAtom("DimToStarboard"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("EPFD"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Raim"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DTE"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Assigned"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("TimeStamp"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MessageNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
|
|
createRelationForType(19,attrList);
|
|
}
|
|
Tuple* tuple = new Tuple(tupleTypes[18]);
|
|
tuple->PutAttribute(0, new CcInt(true,msg->type));
|
|
tuple->PutAttribute(1, new CcInt(true,msg->repeat));
|
|
tuple->PutAttribute(2, new CcInt(true,msg->mmsi));
|
|
tuple->PutAttribute(3, new CcInt(true,msg->sog));
|
|
tuple->PutAttribute(4, new CcInt(true,msg->accuracy));
|
|
tuple->PutAttribute(5, new CcReal(true,msg->longitude));
|
|
tuple->PutAttribute(6, new CcReal(true,msg->latitude));
|
|
tuple->PutAttribute(7, new CcInt(true,msg->cog));
|
|
tuple->PutAttribute(8, new CcInt(true,msg->heading));
|
|
tuple->PutAttribute(9, new CcInt(true,msg->second));
|
|
tuple->PutAttribute(10, new CcString(true,msg->name));
|
|
tuple->PutAttribute(11, new CcInt(true,msg->shiptype));
|
|
tuple->PutAttribute(12, new CcInt(true,msg->dimToBow));
|
|
tuple->PutAttribute(13, new CcInt(true,msg->dimToStern));
|
|
tuple->PutAttribute(14, new CcInt(true,msg->dimToPort));
|
|
tuple->PutAttribute(15, new CcInt(true,msg->dimToStarboard));
|
|
tuple->PutAttribute(16, new CcInt(true,msg->epfd));
|
|
tuple->PutAttribute(17, new CcInt(true,msg->raim));
|
|
tuple->PutAttribute(18, new CcInt(true,msg->dte));
|
|
tuple->PutAttribute(19, new CcInt(true,msg->assigned));
|
|
tuple->PutAttribute(20, new Instant(time));
|
|
tuple->PutAttribute(21, new CcInt(true,count));
|
|
relations[18]->AppendTuple(tuple);
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
|
|
/*
|
|
Message type 24
|
|
|
|
*/
|
|
void writeMessage(aisdecode::Message24* msg){
|
|
if(relations[23]==0){
|
|
ListExpr attrList = nl->OneElemList(
|
|
nl->TwoElemList(nl->SymbolAtom("Type"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
ListExpr last = attrList;
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Repeat"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("PartNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("ShipName"),
|
|
listutils::basicSymbol<CcString>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("ShipType"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("VendorId"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Model"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Serial"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("CallSign"),
|
|
listutils::basicSymbol<CcString>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DimToBow"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DimToStern"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("DimToPort"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(
|
|
nl->SymbolAtom("DimToStarboard"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(
|
|
nl->SymbolAtom("Mothership_MMSI"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("TimeStamp"),
|
|
listutils::basicSymbol<Instant>()));
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("MessageNo"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
createRelationForType(24,attrList);
|
|
}
|
|
Tuple* tuple = new Tuple(tupleTypes[23]);
|
|
tuple->PutAttribute(0, new CcInt(true,msg->type));
|
|
tuple->PutAttribute(1, new CcInt(true,msg->repeat));
|
|
tuple->PutAttribute(2, new CcInt(true,msg->mmsi));
|
|
tuple->PutAttribute(3, new CcInt(true,msg->partno));
|
|
tuple->PutAttribute(4, new CcString(true,msg->shipsname));
|
|
tuple->PutAttribute(5, new CcInt(true,msg->shiptype));
|
|
tuple->PutAttribute(6, new CcInt(true,msg->vendorid));
|
|
tuple->PutAttribute(7, new CcInt(true,msg->model));
|
|
tuple->PutAttribute(8, new CcInt(true,msg->serial));
|
|
tuple->PutAttribute(9, new CcString(true,msg->callsign));
|
|
tuple->PutAttribute(10, new CcInt(true,msg->dimToBow));
|
|
tuple->PutAttribute(11, new CcInt(true,msg->dimToStern));
|
|
tuple->PutAttribute(12, new CcInt(true,msg->dimToPort));
|
|
tuple->PutAttribute(13, new CcInt(true,msg->dimToStarboard));
|
|
tuple->PutAttribute(14, new CcInt(true,msg->mothership_mmsi));
|
|
tuple->PutAttribute(15, new Instant(time));
|
|
tuple->PutAttribute(16, new CcInt(true,count));
|
|
relations[23]->AppendTuple(tuple);
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
|
|
|
|
/*
|
|
General distribution of messages accorsing to their types.
|
|
|
|
*/
|
|
|
|
void processMessage2(aisdecode::MessageBase* msg){
|
|
int t = msg->getType();
|
|
switch(t){
|
|
case 1:
|
|
case 2:
|
|
case 3: writeMessage( (aisdecode::Message1_3*)msg);
|
|
break;
|
|
case 4: writeMessage((aisdecode::Message4*)msg);
|
|
break;
|
|
case 9: writeMessage((aisdecode::Message9*)msg);
|
|
break;
|
|
case 18: writeMessage((aisdecode::Message18*) msg);
|
|
break;
|
|
case 19: writeMessage((aisdecode::Message19*) msg);
|
|
break;
|
|
default: cerr << "found message type " << t
|
|
<< "in processMessages" << endl;
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
int getSeconds(T* msg){
|
|
return msg->second;
|
|
}
|
|
|
|
template<class T>
|
|
bool isValidSecond(T* msg){
|
|
int s = getSeconds(msg);
|
|
return s>=0 && s<=60;
|
|
}
|
|
|
|
|
|
template<class T>
|
|
void processMessage(T* msg){
|
|
processMessage2(msg);
|
|
}
|
|
|
|
template<class T>
|
|
void processMessageWS(T* msg){
|
|
if(time.IsDefined()){
|
|
posMessages.push_back(msg);
|
|
} else {
|
|
processMessage2(msg);
|
|
delete msg;
|
|
}
|
|
}
|
|
|
|
void processPosMessages(datetime::DateTime & old, datetime::DateTime now){
|
|
if(posMessages.empty()){
|
|
return;
|
|
}
|
|
size_t numMessages = posMessages.size();
|
|
datetime::DateTime timeDiff = now - old;
|
|
datetime::DateTime timeDiffPerMsg = timeDiff / numMessages;
|
|
for(size_t i=0;i<numMessages;i++){
|
|
time += timeDiffPerMsg;
|
|
assert(time <= now);
|
|
processMessage2(posMessages[i]);
|
|
delete posMessages[i];
|
|
}
|
|
posMessages.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void processMessage4(aisdecode::Message4* msg){
|
|
|
|
// invalid date
|
|
if( msg->year < 1 || msg->day<1 || msg->month<1 || msg->day>31
|
|
|| msg->month>12){
|
|
noUsedReason=1;
|
|
writeMessage(msg);
|
|
return;
|
|
}
|
|
|
|
if(forbidden.find(msg->mmsi)!=forbidden.end()){ // mmsi not allowed
|
|
noUsedReason=2;
|
|
writeMessage(msg);
|
|
return;
|
|
}
|
|
|
|
|
|
if(!time.IsValid(msg->year, msg->month, msg->day)){
|
|
// invalid date
|
|
noUsedReason=3;
|
|
writeMessage(msg);
|
|
return;
|
|
}
|
|
if(msg->hour>23 || msg->minute>59 || msg->second>59){
|
|
// imvalid time
|
|
noUsedReason=4;
|
|
writeMessage(msg);
|
|
return;
|
|
}
|
|
|
|
datetime::DateTime t(datetime::instanttype);
|
|
t.Set(msg->year, msg->month, msg->day, msg->hour, msg->minute,
|
|
msg->second);
|
|
if(!complete.contains(t)){ // time stamp not allowed
|
|
noUsedReason=5;
|
|
writeMessage(msg);
|
|
return;
|
|
}
|
|
|
|
noUsedReason=0;
|
|
if(!time.IsDefined()){
|
|
// this is the first valid type 4 message
|
|
// this ais will be the master of time
|
|
time=t;
|
|
assert(lastTimes.empty());
|
|
lastTimes[msg->mmsi] =time;
|
|
writeMessage(msg);
|
|
return;
|
|
}
|
|
assert(!lastTimes.empty());
|
|
|
|
datetime::DateTime msgtime(datetime::instanttype);
|
|
msgtime = t;
|
|
|
|
map<int, datetime::DateTime>:: iterator it;
|
|
it = lastTimes.find(msg->mmsi);
|
|
if(it==lastTimes.end()){
|
|
// this ist the first message from this mmsi
|
|
//datetime::DateTime timediff = time - msgtime;
|
|
lastTimes[msg->mmsi] = msgtime;
|
|
writeMessage(msg);
|
|
it = lastTimes.find(msg->mmsi);
|
|
} else {
|
|
if(msgtime <= it->second){
|
|
cout << "MMSI " << msg->mmsi << " goes back in time from "
|
|
<< it->second << " to " << msgtime
|
|
<< "(" << (it->second - msgtime) << ")" << endl;
|
|
noUsedReason=6;
|
|
writeMessage(msg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// we have already stored a time for this mmsi
|
|
// compute the time according to the message time
|
|
it->second = msgtime;
|
|
if(msgtime >= time){
|
|
datetime::DateTime diff = msgtime - time;
|
|
if(diff>complete.threshold){
|
|
noUsedReason = 7;
|
|
writeMessage(msg);
|
|
return;
|
|
}
|
|
processPosMessages(time, msgtime);
|
|
time = msgtime;
|
|
} else { // msgtime < time
|
|
;
|
|
//cout << msg->mmsi << ": back in time for " << (time - msgtime)
|
|
// << endl;
|
|
}
|
|
writeMessage(msg);
|
|
}
|
|
|
|
|
|
|
|
void processMessage1(aisdecode::MessageBase* msg){
|
|
int type = msg->getType();
|
|
switch(type){
|
|
case 1:
|
|
case 2:
|
|
case 3: processMessageWS((aisdecode::Message1_3*)msg);
|
|
break;
|
|
case 4: processMessage4((aisdecode::Message4*)msg);
|
|
delete msg;
|
|
break;
|
|
case 5: writeMessage((aisdecode::Message5*)msg);
|
|
delete msg;
|
|
break;
|
|
case 9: processMessageWS((aisdecode::Message9*)msg);
|
|
break;
|
|
case 12: writeMessage((aisdecode::Message12*)msg);
|
|
delete msg;
|
|
break;
|
|
case 14: writeMessage((aisdecode::Message14*)msg);
|
|
delete msg;
|
|
break;
|
|
case 18: processMessageWS((aisdecode::Message18*)msg);
|
|
break;
|
|
case 19: processMessageWS((aisdecode::Message19*)msg);
|
|
break;
|
|
case 24: writeMessage((aisdecode::Message24*)msg);
|
|
delete msg;
|
|
break;
|
|
default: cerr << "Secondo: message type " << type
|
|
<< "not implemented yet" << endl;
|
|
delete msg;
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
24.99.6 Value Mapping
|
|
|
|
*/
|
|
template<class T>
|
|
int importaisVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = (CcInt*) result.addr;
|
|
T* filename = (T*) args[0].addr;
|
|
if(!filename->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
CcString* prefix = (CcString*) args[1].addr;
|
|
if(!prefix->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
aisimportInfo ais(filename->GetValue(), prefix->GetValue());
|
|
res->Set(true,ais());
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping importaisVM[] = {
|
|
importaisVMT<CcString>,
|
|
importaisVMT<FText>
|
|
};
|
|
|
|
/*
|
|
24.99.7 Selection Function
|
|
|
|
*/
|
|
int importaisSelect(ListExpr args){
|
|
return CcString::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
/*
|
|
24.99.7 Specifikation
|
|
|
|
*/
|
|
OperatorSpec importaisSpec(
|
|
"{string, text} x string -> int ",
|
|
"importais(filename, prefix)",
|
|
"Creates a set of relations prefixed by prefix according "
|
|
"to ais messages found in the file. Returns the total amount "
|
|
"of messages in the file.",
|
|
"query importais('aisexample.txt',\"AIS\")"
|
|
);
|
|
|
|
/*
|
|
24.99.7 Operator instance
|
|
|
|
*/
|
|
Operator importaisOp(
|
|
"importais",
|
|
importaisSpec.getStr(),
|
|
2,
|
|
importaisVM,
|
|
importaisSelect,
|
|
importaisTM
|
|
);
|
|
|
|
|
|
/*
|
|
24.100 exportBinCSV
|
|
|
|
This operator receives a tuple stream, a string or text,
|
|
and some attribute names. The second argument is the
|
|
filename of the file to be created.
|
|
The mentioned attribute names
|
|
must be in kind CVSEXPORTABLE. This operator creates
|
|
a csv file in format Nr , <ATTR>, <TUPLE>
|
|
Where Nr is just a running number, <ATTR> are the
|
|
attributes given in the query and <TUPLE> is the
|
|
base64 coded binary tuple representation.
|
|
|
|
*/
|
|
|
|
ListExpr exportBinCSVTM(ListExpr args){
|
|
if(!nl->HasMinLength(args,2)){
|
|
return listutils::typeError("at least two arguments required");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError("the first argument must be a tuple stream");
|
|
}
|
|
ListExpr fn = nl->Second(args);
|
|
if(! CcString::checkType(fn) && !FText::checkType(fn)){
|
|
return listutils::typeError("the second argument must be of "
|
|
"type string or text");
|
|
}
|
|
ListExpr attrList = nl->Second(nl->Second(nl->First(args)));
|
|
ListExpr attrs = nl->Rest(nl->Rest(args));
|
|
ListExpr appendList = nl->TheEmptyList();
|
|
ListExpr appendLast = nl->TheEmptyList();
|
|
|
|
while(!nl->IsEmpty(attrs)){
|
|
ListExpr attr = nl->First(attrs);
|
|
attrs = nl->Rest(attrs);
|
|
if(nl->AtomType(attr) != SymbolType){
|
|
return listutils::typeError("Invalid attribute name");
|
|
}
|
|
string a = nl->SymbolValue(attr);
|
|
ListExpr attrType;
|
|
int index = listutils::findAttribute(attrList, a, attrType);
|
|
if(!index){
|
|
return listutils::typeError("Attribute " + a
|
|
+ " not part of the tuple");
|
|
}
|
|
ListExpr errorInfo = listutils::emptyErrorInfo();
|
|
if(!am->CheckKind("CSVEXPORTABLE", attrType, errorInfo)){
|
|
return listutils::typeError("Attribute " + a
|
|
+ " not in kind CSVEXPORTABLE");
|
|
}
|
|
|
|
if(nl->IsEmpty(appendList)){
|
|
appendList = nl->OneElemList(nl->IntAtom(index-1));
|
|
appendLast = appendList;
|
|
} else {
|
|
appendLast = nl->Append(appendLast, nl->IntAtom(index-1));
|
|
}
|
|
appendLast = nl->Append(appendLast, nl->StringAtom(a));
|
|
appendLast = nl->Append(appendLast, nl->TextAtom(nl->ToString(attrType)));
|
|
}
|
|
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbols::APPEND()),
|
|
appendList,
|
|
nl->First(args));
|
|
|
|
}
|
|
|
|
|
|
class exportBinCSVInfo{
|
|
|
|
public:
|
|
exportBinCSVInfo( Word _stream, string& _file,
|
|
vector<pair<pair<string, string>, int> >& _attrs,
|
|
ListExpr tupleDescr):
|
|
stream(_stream),base64(' ')
|
|
{
|
|
stream.open();
|
|
out.open(_file.c_str());
|
|
// write header if file is good
|
|
if(out){
|
|
out << "# No: int";
|
|
for(size_t i=0;i<_attrs.size();i++){
|
|
pair<pair<string, string>, int> v = _attrs[i];
|
|
out << "," << v.first.first << ":" << v.first.second;
|
|
attrs.push_back(v.second);
|
|
}
|
|
out << ", T : " << nl->ToString(tupleDescr) << endl;
|
|
}
|
|
no = 0;
|
|
}
|
|
|
|
~exportBinCSVInfo(){
|
|
stream.close();
|
|
out.close();
|
|
}
|
|
|
|
Tuple* next(){
|
|
if(!out) return 0;
|
|
Tuple* tuple = stream.request();
|
|
if(tuple){
|
|
writeTuple(tuple);
|
|
}
|
|
return tuple;
|
|
}
|
|
|
|
|
|
private:
|
|
Stream<Tuple> stream;
|
|
Base64 base64;
|
|
ofstream out;
|
|
int no;
|
|
vector<int> attrs;
|
|
|
|
|
|
void writeTuple(Tuple* t){
|
|
out << no ;
|
|
for(size_t i=0;i<attrs.size();i++){
|
|
Attribute* attr = t->GetAttribute(attrs[i]);
|
|
string s = attr->getCsvStr();
|
|
out << ", " << s;
|
|
}
|
|
out << ", " << getBase64Data(t) << endl;
|
|
}
|
|
|
|
string getBase64Data(Tuple* t){
|
|
size_t size;
|
|
char* binData = t->GetBinRep(size);
|
|
string res;
|
|
base64.encode(binData, size,res);
|
|
delete[] binData;
|
|
return res;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
template<class T>
|
|
int exportBinCSVVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
exportBinCSVInfo* li = (exportBinCSVInfo*) local.addr;
|
|
switch(message){
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
int noAttrs = (qp->GetNoSons(s)-2) / 4;
|
|
T* fn = (T*) args[1].addr;
|
|
if(!fn->IsDefined()){
|
|
return 0;
|
|
}
|
|
string f = fn->GetValue();
|
|
|
|
int start = 2+noAttrs;
|
|
int end = qp->GetNoSons(s);
|
|
vector<pair<pair<string,string>,int> > v;
|
|
for(int i=start;i<end;i=i+3){
|
|
int no = ((CcInt*)args[i].addr)->GetValue();
|
|
string name = ((CcString*)args[i+1].addr)->GetValue();
|
|
string type = ((FText*)args[i+2].addr)->GetValue();
|
|
v.push_back(make_pair(make_pair(name,type),no));
|
|
}
|
|
local.addr = new exportBinCSVInfo(args[0],f,v,
|
|
nl->Second(qp->GetType(s)));
|
|
return 0;
|
|
}
|
|
case REQUEST:
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
case CLOSE:
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int exportBinCSVSelect(ListExpr args){
|
|
return CcString::checkType(nl->Second(args))?0:1;
|
|
}
|
|
|
|
ValueMapping exportBinCSVVM[] = {
|
|
exportBinCSVVMT<CcString>,
|
|
exportBinCSVVMT<FText>
|
|
};
|
|
|
|
|
|
OperatorSpec exportBinCSVSpec(
|
|
"stream(tuple) x {string,text} x INDENT* -> stream(tuple)",
|
|
" _ exportBinCSV[_,_...]",
|
|
" Writes a tuple stream to a csv file. "
|
|
"The first argument is the stream of tuples to be write, "
|
|
"the second argument is the name of the file, the remaining "
|
|
"arguments that represents attribute names to write additionally as "
|
|
"text into the file. The created file cosists of lines of the form "
|
|
"int, (DATA,)* <TUPLE> where the first entry is a runnning number,"
|
|
"the following entries are the specified attributea and the last "
|
|
"value is the base64 coded binary representation of the whole tuple.",
|
|
"query plz feed exportBinCSV['plz.bincsv', PLZ] count"
|
|
);
|
|
|
|
Operator exportBinCSVOp(
|
|
"exportBinCSV",
|
|
exportBinCSVSpec.getStr(),
|
|
2,
|
|
exportBinCSVVM,
|
|
exportBinCSVSelect,
|
|
exportBinCSVTM
|
|
);
|
|
|
|
|
|
/*
|
|
24.101 Operator ~importBinCSVSimple~
|
|
|
|
*/
|
|
ListExpr importBinCSVSimpleTM(ListExpr args){
|
|
|
|
if(!nl->HasLength(args,3)){
|
|
return listutils::typeError("three arguments expected");
|
|
}
|
|
if(!listutils::checkUsesArgsInTypeMapping(args)){
|
|
return listutils::typeError("internal error");
|
|
}
|
|
|
|
ListExpr fn = nl->First(nl->First(args));
|
|
if(!CcString::checkType(fn) && !FText::checkType(fn)){
|
|
return listutils::typeError("first arg must be a string or a text");
|
|
}
|
|
ListExpr te = nl->First(nl->Second(args));
|
|
if(!Relation::checkType(te) && !FText::checkType(te)){
|
|
return listutils::typeError("second arg must be a relation or a text");
|
|
}
|
|
|
|
if(!CcInt::checkType(nl->First(nl->Third(args)))){
|
|
return listutils::typeError("third argument must be of type int");
|
|
}
|
|
ListExpr tupleDescr = nl->TheEmptyList();
|
|
if(FText::checkType(te)){
|
|
ListExpr tev = nl->Second(nl->Second(args));
|
|
Word queryResult ;
|
|
string typeString = " " ;
|
|
string errorString = " " ;
|
|
bool correct ;
|
|
bool evaluable ;
|
|
bool defined ;
|
|
bool isFunction ;
|
|
qp->ExecuteQuery(tev, queryResult, typeString,
|
|
errorString, correct, evaluable, defined, isFunction );
|
|
if(!correct || !evaluable || !defined || isFunction ){
|
|
return listutils::typeError ( " could not extract filename ( " +
|
|
errorString + " ) " );
|
|
}
|
|
FText* fn = (FText*) queryResult.addr;
|
|
if(!fn->IsDefined()){
|
|
fn->DeleteIfAllowed();
|
|
return listutils::typeError("undefined tuple type description");
|
|
}
|
|
string tt = fn->GetValue();
|
|
fn->DeleteIfAllowed();
|
|
if(!nl->ReadFromString(tt,tupleDescr)){
|
|
return listutils::typeError("tuple description is not a valid list");
|
|
}
|
|
if(!Tuple::checkType(tupleDescr)){
|
|
return listutils::typeError("description is not a valid tuple");
|
|
}
|
|
} else {
|
|
tupleDescr = nl->Second(te);
|
|
}
|
|
return nl->TwoElemList(listutils::basicSymbol<Stream<Tuple> >(),
|
|
tupleDescr);
|
|
}
|
|
|
|
|
|
class importBinCSVSimpleInfo{
|
|
|
|
public:
|
|
importBinCSVSimpleInfo(string _fileName, int _pos, TupleType* _tt){
|
|
in.open(_fileName.c_str());
|
|
pos = _pos;
|
|
tt = _tt;
|
|
tt->IncReference();
|
|
id = 1;
|
|
getline(in,line); // ignore first line
|
|
}
|
|
|
|
~importBinCSVSimpleInfo(){
|
|
in.close();
|
|
tt->DeleteIfAllowed();
|
|
}
|
|
|
|
Tuple* next(){
|
|
while(!in.eof() && in){
|
|
getline(in,line);
|
|
if(line.length()>0){
|
|
Tuple* res = createTuple(line);
|
|
if(res){
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
ifstream in;
|
|
TupleType* tt;
|
|
int pos;
|
|
string line;
|
|
Base64 base64;
|
|
TupleId id;
|
|
|
|
|
|
Tuple* createTuple(string & line){
|
|
// extract base 64 rep of the tuple
|
|
stringutils::StringTokenizer st(line,",");
|
|
string byterep;
|
|
for(int i=0; i< pos; i++){
|
|
if(!st.hasNextToken()){
|
|
return 0;
|
|
}
|
|
byterep = st.nextToken();
|
|
}
|
|
stringutils::trim(byterep);
|
|
if(byterep.empty()) return 0;
|
|
|
|
int size = base64.sizeDecoded(byterep.size());
|
|
char* bytes = new char[size];
|
|
try{
|
|
size = base64.decode(byterep, bytes);
|
|
} catch(...){
|
|
cout << "decoding base64 failed" << endl;
|
|
delete[] bytes;
|
|
return 0;
|
|
}
|
|
Tuple* res = getTuple(bytes);
|
|
delete[] bytes;
|
|
return res;
|
|
}
|
|
|
|
|
|
Tuple* getTuple(char* bytes){
|
|
Tuple* res = new Tuple(tt);
|
|
res->ReadFromBin(0, bytes );
|
|
res->SetTupleId(id);
|
|
id++;
|
|
return res;
|
|
}
|
|
};
|
|
|
|
template<class T>
|
|
int importBinCSVSimpleVMT(Word* args, Word& result,
|
|
int message, Word& local, Supplier s){
|
|
|
|
importBinCSVSimpleInfo* li = (importBinCSVSimpleInfo*) local.addr;
|
|
switch(message){
|
|
case INIT:
|
|
qp->GetLocal2(s).addr =
|
|
new TupleType(nl->Second(GetTupleResultType(s)));
|
|
return 0;
|
|
case FINISH: {
|
|
TupleType* tt = (TupleType*)qp->GetLocal2(s).addr;
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr =0;
|
|
}
|
|
return 0;
|
|
}
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
T* fn = (T*) args[0].addr;
|
|
if(!fn->IsDefined()){
|
|
return 0;
|
|
}
|
|
TupleType* tt = (TupleType*)qp->GetLocal2(s).addr;
|
|
CcInt* pos = (CcInt*) args[2].addr;
|
|
if(!pos->IsDefined()){
|
|
return 0;
|
|
}
|
|
local.addr = new importBinCSVSimpleInfo(fn->GetValue(),
|
|
pos->GetValue(),tt);
|
|
return 0;
|
|
}
|
|
case REQUEST: result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
|
|
case CLOSE :
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
int importBinCSVSimpleSelect(ListExpr args){
|
|
return CcString::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
ValueMapping importBinCSVSimpleVM[] = {
|
|
importBinCSVSimpleVMT<CcString>,
|
|
importBinCSVSimpleVMT<FText>
|
|
};
|
|
|
|
OperatorSpec importBinCSVSimpleSpec(
|
|
"{string,text} x {rel,text} x int -> stream(tuple)",
|
|
"_ importBinCSVSimple[_,_] ",
|
|
"Imports a relation that is write into a CSV format and the "
|
|
"tuples are base64 code binary. The last argument give the "
|
|
"position of the tuple within a csv line.",
|
|
" query 'plz.bincsv' importBinCSVSimple[plz, 2] count"
|
|
);
|
|
|
|
|
|
Operator importBinCSVSimpleOp(
|
|
"importBinCSVSimple",
|
|
importBinCSVSimpleSpec.getStr(),
|
|
2,
|
|
importBinCSVSimpleVM,
|
|
importBinCSVSimpleSelect,
|
|
importBinCSVSimpleTM
|
|
);
|
|
|
|
|
|
/*
|
|
24.102 Operator ~geojson2line~
|
|
|
|
*/
|
|
|
|
/*
|
|
24.102.1 Type Mapping function
|
|
|
|
The signature is text x bool -> line
|
|
|
|
*/
|
|
|
|
ListExpr Geojson2lineTypeMap(ListExpr args){
|
|
|
|
if(nl->ListLength(args) != 2) {
|
|
return listutils::typeError("two arguments exptected");
|
|
}
|
|
|
|
if(FText::checkType(nl->First(args)) &&
|
|
CcBool::checkType(nl->Second(args))) {
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
}
|
|
|
|
return listutils::typeError("text expected");
|
|
}
|
|
|
|
/*
|
|
24.102.2 Value Mapping function
|
|
|
|
*/
|
|
int Geojson2line(Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
|
|
result = qp->ResultStorage(s);
|
|
|
|
FText* text = static_cast<FText*>(args[0].addr);
|
|
CcBool* autocloseBool = static_cast<CcBool*>(args[1].addr);
|
|
|
|
Line* res = static_cast<Line*>(result.addr);
|
|
vector<double> points;
|
|
|
|
if(! text -> IsDefined()) {
|
|
cerr << "Input text is not defined" << endl;
|
|
res -> SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
if(! autocloseBool -> IsDefined()) {
|
|
cerr << "Autoclose is not defined" << endl;
|
|
res -> SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
string data = text -> GetValue();
|
|
bool autoclose = autocloseBool -> GetValue();
|
|
|
|
bool containsPolygon = data.find("\"type\":\"Polygon\"")
|
|
!= std::string::npos;
|
|
|
|
bool containsLinestring = data.find("\"type\":\"LineString\"")
|
|
!= std::string::npos;
|
|
|
|
if(! containsPolygon && ! containsLinestring) {
|
|
cerr << "Input does not contain a Polygon or a LineString" << endl;
|
|
res -> SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
const char* dataArray = data.c_str();
|
|
const char* start = strstr(dataArray, "[[");
|
|
|
|
if(start == NULL) {
|
|
cerr << "Input is not valid GEOJson" << endl;
|
|
res -> SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
string buffer = "";
|
|
char lastChar = '\0';
|
|
|
|
for(int i = start - dataArray; dataArray[i] != '\0'; i++) {
|
|
char curChar = data[i];
|
|
|
|
if(curChar == ']' && lastChar == ']') {
|
|
if(! buffer.empty()) {
|
|
points.push_back(stod(buffer));
|
|
}
|
|
break;
|
|
} else if(curChar == ',' || curChar == ']') {
|
|
if(! buffer.empty()) {
|
|
points.push_back(stod(buffer));
|
|
buffer = "";
|
|
}
|
|
} else if(curChar == '[') {
|
|
// Ignore
|
|
} else {
|
|
buffer += curChar;
|
|
}
|
|
|
|
lastChar = curChar;
|
|
}
|
|
|
|
if(points.size() % 2 != 0) {
|
|
cerr << "Got an uneven amount of points" << endl;
|
|
res -> SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
vector<SimplePoint> simplepoints;
|
|
|
|
// Convert to points
|
|
for(size_t i = 0; i < points.size(); i = i + 2) {
|
|
double x = points[i];
|
|
double y = points[i+1];
|
|
SimplePoint point = SimplePoint(x, y);
|
|
simplepoints.push_back(point);
|
|
}
|
|
|
|
// Check for closed path
|
|
if(containsPolygon) {
|
|
if(simplepoints[0] != simplepoints[simplepoints.size() - 1]) {
|
|
if(! autoclose) {
|
|
cerr << "Error: Region is not closed" << endl;
|
|
res -> SetDefined(false);
|
|
return 0;
|
|
} else {
|
|
// Close line
|
|
simplepoints.push_back(simplepoints[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
res -> SetDefined(true);
|
|
res -> StartBulkLoad();
|
|
res -> Resize(simplepoints.size());
|
|
for(size_t i = 0; i < simplepoints.size() - 1; ++i) {
|
|
SimplePoint p1 = simplepoints[i];
|
|
SimplePoint p2 = simplepoints[i+1];
|
|
HalfSegment hs1(true,p1.getPoint(),p2.getPoint());
|
|
HalfSegment hs2(false,p1.getPoint(),p2.getPoint());
|
|
hs1.attr.edgeno = i;
|
|
hs2.attr.edgeno = i;
|
|
(*res) += hs1;
|
|
(*res) += hs2;
|
|
}
|
|
|
|
res -> EndBulkLoad();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
24.102.3 Specification of the operator ~geojson2line~
|
|
|
|
*/
|
|
const string Geojson2lineSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( "
|
|
"<text>text x bool -> region</text--->"
|
|
"<text>geojson2line (_, _)</text--->"
|
|
"<text> The operator geojson2line extracts"
|
|
" all points from a GeoJSON polygon and creates"
|
|
" a line. The first parameter is the GeoJSON"
|
|
" input. The second parameter determines if the"
|
|
" line should be automatically closed"
|
|
" (if close segment is missing in GeoJSON).</text--->"
|
|
"<text>query geojson2line([...])</text--->"
|
|
") )";
|
|
|
|
/*
|
|
24.102.4 Definition of the operator ~geojson2line~
|
|
|
|
*/
|
|
Operator geojson2line (
|
|
"geojson2line",
|
|
Geojson2lineSpec,
|
|
Geojson2line,
|
|
Operator::SimpleSelect,
|
|
Geojson2lineTypeMap );
|
|
|
|
/*
|
|
24.103 Operator ~geojson2point~
|
|
|
|
*/
|
|
|
|
/*
|
|
24.103.1 Type Mapping function
|
|
|
|
The signature is text -> point
|
|
|
|
*/
|
|
|
|
ListExpr Geojson2pointTypeMap(ListExpr args){
|
|
|
|
if(nl->ListLength(args) != 1) {
|
|
return listutils::typeError("one argument exptected");
|
|
}
|
|
|
|
if(FText::checkType(nl->First(args))) {
|
|
return nl->SymbolAtom(Point::BasicType());
|
|
}
|
|
|
|
return listutils::typeError("text expected");
|
|
}
|
|
|
|
/*
|
|
24.103.2 Value Mapping function
|
|
|
|
*/
|
|
int Geojson2point(Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
|
|
result = qp->ResultStorage(s);
|
|
|
|
FText* text = static_cast<FText*>(args[0].addr);
|
|
|
|
Point* res = static_cast<Point*>(result.addr);
|
|
vector<double> points;
|
|
|
|
if(! text -> IsDefined()) {
|
|
cerr << "Input text is not defined" << endl;
|
|
res -> SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
string data = text -> GetValue();
|
|
|
|
bool contains = data.find("\"type\":\"Point\"") != std::string::npos;
|
|
|
|
if(! contains) {
|
|
cerr << "Input does not contain a Point" << endl;
|
|
res -> SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
const char* dataArray = data.c_str();
|
|
const char* startStr = "coordinates\":[";
|
|
const char* start = strstr(dataArray, startStr);
|
|
|
|
if(start == NULL) {
|
|
cerr << "Input is not valid GEOJson" << endl;
|
|
res -> SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
string buffer = "";
|
|
const char* startPos = start + strlen(startStr);
|
|
|
|
for(int i = startPos - dataArray; dataArray[i] != '\0'; i++) {
|
|
char curChar = dataArray[i];
|
|
|
|
if(curChar == ']') {
|
|
if(! buffer.empty()) {
|
|
points.push_back(stod(buffer));
|
|
}
|
|
break;
|
|
} else if(curChar == ',' || curChar == ']') {
|
|
if(! buffer.empty()) {
|
|
cout << "Data: " << buffer << endl;
|
|
points.push_back(stod(buffer));
|
|
buffer = "";
|
|
}
|
|
} else {
|
|
buffer += curChar;
|
|
}
|
|
}
|
|
|
|
if(points.size() != 2) {
|
|
cerr << "Got an unexpected amount of points" << endl;
|
|
res -> SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
res->Set(points[0], points[1]); // also sets to DEFINED
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
24.103.3 Specification of the operator ~geojson2point~
|
|
|
|
*/
|
|
const string Geojson2pointSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( "
|
|
"<text>text -> point</text--->"
|
|
"<text>geojson2point (_)</text--->"
|
|
"<text> The operator geojson2ponint extracts"
|
|
" a point from a GeoJSON text."
|
|
" The first parameter is the GeoJSON</text--->"
|
|
"<text>query geojson2point([...])</text--->"
|
|
") )";
|
|
|
|
|
|
|
|
/*
|
|
24.103.4 Definition of the operator ~geojson2point~
|
|
|
|
*/
|
|
Operator geojson2point (
|
|
"geojson2point",
|
|
Geojson2pointSpec,
|
|
Geojson2point,
|
|
Operator::SimpleSelect,
|
|
Geojson2pointTypeMap );
|
|
|
|
|
|
|
|
/*
|
|
25 Creating the Algebra
|
|
|
|
*/
|
|
|
|
class ImExAlgebra : public Algebra
|
|
{
|
|
public:
|
|
ImExAlgebra() : Algebra()
|
|
{
|
|
AddOperator( &csvexport );
|
|
AddOperator( &shpexport );
|
|
AddOperator( &db3export );
|
|
AddOperator( &shptype );
|
|
AddOperator( &shpimport );
|
|
AddOperator( &shpimport2 );
|
|
shpimport2.SetUsesArgsInTypeMapping();
|
|
AddOperator( &dbtype );
|
|
AddOperator( &dbimport);
|
|
AddOperator( &dbimport2);
|
|
dbimport2.SetUsesArgsInTypeMapping();
|
|
AddOperator( &saveObject);
|
|
AddOperator( &csvimport);
|
|
AddOperator( &csvimport2);
|
|
csvimport2.SetUsesArgsInTypeMapping();
|
|
AddOperator( &isFile);
|
|
AddOperator( &removeFile);
|
|
AddOperator( &createDirectory);
|
|
AddOperator( &fileSize);
|
|
AddOperator( &isDirectory);
|
|
AddOperator( &writeFile);
|
|
AddOperator( &readFile);
|
|
AddOperator( &moveFile);
|
|
AddOperator( ©File);
|
|
AddOperator( &basenameOp);
|
|
AddOperator( &getDirectory);
|
|
AddOperator( &toCSVtext);
|
|
AddOperator( &fromCSVtext);
|
|
AddOperator( &getPID);
|
|
AddOperator( &getSecondoVersion);
|
|
AddOperator( &getBDBVersion);
|
|
AddOperator( &getSecondoPlatform);
|
|
AddOperator( &getPageSize);
|
|
AddOperator( &nmeaimport);
|
|
nmeaimport.SetUsesArgsInTypeMapping();
|
|
AddOperator( &nmeaimport_line);
|
|
nmeaimport_line.SetUsesArgsInTypeMapping();
|
|
AddOperator( &get_lines);
|
|
AddOperator( &sqlExport);
|
|
AddOperator( &importGHT1);
|
|
AddOperator( &removeDirectoryOp);
|
|
#ifndef SECONDO_WIN32
|
|
AddOperator( &rtf2txtfile);
|
|
#endif
|
|
AddOperator(&shpBoxOp);
|
|
AddOperator(&shpCollectOp);
|
|
AddOperator(&db3CollectOp);
|
|
AddOperator(&createShxOp);
|
|
AddOperator(&noShpRecordsOp);
|
|
AddOperator(&noDB3RecordsOp);
|
|
AddOperator(&extractShpPartOp);
|
|
AddOperator(&extractDB3PartOp);
|
|
|
|
AddOperator(&splitShpOp);
|
|
AddOperator(&splitDB3Op);
|
|
|
|
AddOperator(&importaisOp);
|
|
importaisOp.SetUsesArgsInTypeMapping();
|
|
|
|
AddOperator(&exportBinCSVOp);
|
|
AddOperator(&importBinCSVSimpleOp);
|
|
importBinCSVSimpleOp.SetUsesArgsInTypeMapping();
|
|
importBinCSVSimpleOp.enableInitFinishSupport();
|
|
|
|
AddOperator(&geojson2line);
|
|
AddOperator(&geojson2point);
|
|
}
|
|
~ImExAlgebra() {};
|
|
};
|
|
|
|
/*
|
|
9 Initialization
|
|
|
|
*/
|
|
|
|
extern "C"
|
|
Algebra*
|
|
InitializeImExAlgebra( NestedList* nlRef, QueryProcessor* qpRef )
|
|
{
|
|
nl = nlRef;
|
|
qp = qpRef;
|
|
return (new ImExAlgebra());
|
|
}
|
|
|
|
|