Antlr4 param-value parsing - antlr4
I'm new to antlr and trying to parse a file of mine (IBM Datastage export).
I have a working grammar but i'm not fully satisfied and I'm not sure I'm using antlr the right way :(
Here is an example of source file :
BEGIN HEADER
CharacterSet "CP1252"
ExportingTool "IBM InfoSphere DataStage Export"
ToolVersion "8"
ServerName "MIAIBV240"
ToolInstanceID "DFDMGBL2"
MDISVersion "1.0"
Date "2018-02-12"
Time "17.32.28"
ServerVersion "8.7"
END HEADER
BEGIN DSJOB
Identifier "job_FDM_WVD_NET_SALES_DSC_STG_REPL_Load"
DateModified "2018-02-12"
TimeModified "17.32.24"
BEGIN DSRECORD
Identifier "C52"
OLEType "CContainerStage"
Readonly "0"
Name "ShcFileAudit_Verif_Data"
NextID "3"
BEGIN DSSUBRECORD
Name "FDM_CMN"
Description "#FDM_CMN#"
ValueType "0"
END DSSUBRECORD
BEGIN DSSUBRECORD
Name "FILE_NAME"
FullDescription =+=+=+=#------------------------------------------------------------------------------- Description:
Some desc here ....
# Date Issue Version
=+=+=+=
JobVersion "56.0.0"
ControlAfterSubr "0"
Parameters "CParameters"
END DSSUBRECORD
END DSRECORD
END DSJOB
As you can see it is mostly Key/value pairs.
I use this grammar :
grammar dsxGrammar;
dsxFile : headerDeclaration? jobDeclaration* EOF;
headerDeclaration : BEGIN HEADER paramHeader* END HEADER;
jobDeclaration : BEGIN DSJOB paramJob* recordDeclaration* paramJob* END DSJOB;
recordDeclaration : BEGIN DSRECORD (paramRecord | subrecordDeclaration )* END DSRECORD;
subrecordDeclaration : BEGIN DSSUBRECORD paramSubRecord* END DSSUBRECORD;
paramHeader : PNAME PVALUE;
paramJob : PNAME PVALUE;
paramRecord : PNAME PVALUE;
paramSubRecord : PNAME PVALUE;
//PVALUE : '"' Text '"';
PVALUE : '"' .*? '"';
BEGIN : 'BEGIN';
END : 'END';
HEADER : 'HEADER';
DSJOB : 'DSJOB';
DSRECORD : 'DSRECORD';
DSSUBRECORD : 'DSSUBRECORD';
ORCHCODE : 'OrchestrateCode =+=+=+=' .*? '=+=+=+=' -> skip;
FULLDESC : 'FullDescription =+=+=+=' .*? '=+=+=+=' -> skip;
VALUECODE : 'Value =+=+=+=' .*? '=+=+=+=' -> skip;
EXPRCODE : 'Expression =+=+=+=' .*? '=+=+=+=' -> skip;
DERIVCODE : 'Derivation =+=+=+=' .*? '=+=+=+=' -> skip;
WS : [ \t\r\n\u000C]+ -> skip;
SPACE : ' ';
COMMENT : '/*' .*? '*/' -> skip;
LINE_COMMENT : '//' ~[\r\n]* -> skip;
//Identifier : NONDIGIT ( NONDIGIT | DIGIT)*;
fragment Text : NONDIGIT ( SPACE | NONDIGIT | DIGIT)*;
fragment NONDIGIT : [a-zA-Z_] ;
fragment DIGIT : [0-9];
PNAME : ~["\\ \t\r\n]+;
It's working but i have to parse PNAME and PVALUE afterwards in my python code to extract double quotes, etc...
Is it the right way to do or I can do something with a Text fragment ?
I would like to have access to HeadProperty.PNAME and HeadProperty.PVALUE for example.
Thanks for your help !
[...] Is it the right way to do or I can do something with a Text fragment ?
Not in the grammar (at least, not without using target specific code). You'll have to change the text in a listener or visitor. In your case, I'd go for a visitor. The code below is in Java, but should be easily converted to Python.
First a couple of remarks about your current grammar:
lexer rules like these 'OrchestrateCode =+=+=+=' can be fragile. If the input contains something like this: OrchestrateCode =+=+=+= (2 spaces), it won't be tokenised properly
your rule SPACE : ' '; is only used in a fragment, so it should also be a fragment
you're repeating PNAME PVALUE quite a lot in your parser rules: I'd create a keyValues rule for it (this also simplifies the visitor which I'll add below)
the same for =+=+=+= in your lexer rule: I'd create a fragment that matches this (DRY!)
It looks like a lot of feedback, but they're all minor points. Given that you're new to ANTLR, I think you did a good job.
OK, given the points above, your grammar could look like this:
grammar dsxGrammar;
dsxFile : headerDeclaration? jobDeclaration* EOF;
headerDeclaration : BEGIN HEADER paramHeaders=keyValues END HEADER;
jobDeclaration : BEGIN DSJOB paramJob+=keyValues recordDeclaration* paramJob+=keyValues END DSJOB;
recordDeclaration : BEGIN DSRECORD ( paramRecord+=keyValue | subrecordDeclaration )* END DSRECORD;
subrecordDeclaration : BEGIN DSSUBRECORD paramSubRecords=keyValues END DSSUBRECORD;
keyValues : keyValue*;
keyValue : PNAME PVALUE;
PVALUE : '"' .*? '"';
BEGIN : 'BEGIN';
END : 'END';
HEADER : 'HEADER';
DSJOB : 'DSJOB';
DSRECORD : 'DSRECORD';
DSSUBRECORD : 'DSSUBRECORD';
ORCHCODE : 'OrchestrateCode' SPACE* SEPARATOR .*? SEPARATOR -> skip;
FULLDESC : 'FullDescription' SPACE* SEPARATOR .*? SEPARATOR -> skip;
VALUECODE : 'Value' SPACE* SEPARATOR .*? SEPARATOR -> skip;
EXPRCODE : 'Expression' SPACE* SEPARATOR .*? SEPARATOR -> skip;
DERIVCODE : 'Derivation' SPACE* SEPARATOR .*? SEPARATOR -> skip;
WS : SPACE+ -> skip;
COMMENT : '/*' .*? '*/' -> skip;
LINE_COMMENT : '//' ~[\r\n]* -> skip;
PNAME : ~["\\ \t\r\n]+;
fragment NONDIGIT : [a-zA-Z_] ;
fragment DIGIT : [0-9];
fragment SPACE : [ \t\r\n\u000C];
fragment SEPARATOR : '=+=+=+=';
You might notice the labels in the parser rules, like paramSubRecords=... and paramJob+=.... These are good for 2 things:
readability of your grammar (keyValues doesn't mean much, while it's clear what keyValues really is in case paramSubRecords=keyValues is written)
in your listener or visitor, you can reference these labeled children from a parent context. Say you have the rule
foo : first=PNAME second=PNAME;
then you can do this in a visitor:
#Override
public Object visitFoo(dsxGrammarParser.FooContext ctx) {
String firstName = ctx.first.getText();
String secondName = ctx.second.getText();
...
}
And if you have a += in there like this:
foo : allJobs+=jobDeclaration+ PNAME allJobs+=jobDeclaration+;
then all jobDeclaration contexts are added in a java.util.List you can use in the parent context:
#Override
public Object visitFoo(dsxGrammarParser.FooContext ctx) {
List<dsxGrammarParser.JobDeclarationContext> jobs = ctx.allJobs;
...
}
If you now generate a visitor from the updated grammar, you can test it like this:
public class Main {
public static void main(String[] args) {
String source = "BEGIN HEADER\n" +
" CharacterSet \"CP1252\"\n" +
" ExportingTool \"IBM InfoSphere DataStage Export\"\n" +
" ToolVersion \"8\"\n" +
" ServerName \"MIAIBV240\"\n" +
" ToolInstanceID \"DFDMGBL2\"\n" +
" MDISVersion \"1.0\"\n" +
" Date \"2018-02-12\"\n" +
" Time \"17.32.28\"\n" +
" ServerVersion \"8.7\"\n" +
"END HEADER\n" +
"BEGIN DSJOB\n" +
" Identifier \"job_FDM_WVD_NET_SALES_DSC_STG_REPL_Load\"\n" +
" DateModified \"2018-02-12\"\n" +
" TimeModified \"17.32.24\"\n" +
" BEGIN DSRECORD\n" +
" Identifier \"C52\"\n" +
" OLEType \"CContainerStage\"\n" +
" Readonly \"0\"\n" +
" Name \"ShcFileAudit_Verif_Data\"\n" +
" NextID \"3\"\n" +
" BEGIN DSSUBRECORD\n" +
" Name \"FDM_CMN\"\n" +
" Description \"#FDM_CMN#\"\n" +
" ValueType \"0\"\n" +
" END DSSUBRECORD\n" +
" BEGIN DSSUBRECORD\n" +
" Name \"FILE_NAME\"\n" +
" FullDescription =+=+=+=#------------------------------------------------------------------------------- Description:\n" +
"\n" +
"Some desc here .... \n" +
"# Date Issue Version \n" +
"=+=+=+=\n" +
" JobVersion \"56.0.0\"\n" +
" ControlAfterSubr \"0\"\n" +
" Parameters \"CParameters\"\n" +
" END DSSUBRECORD\n" +
"END DSRECORD\n" +
"END DSJOB";
dsxGrammarLexer lexer = new dsxGrammarLexer(CharStreams.fromString(source));
dsxGrammarParser parser = new dsxGrammarParser(new CommonTokenStream(lexer));
DsxFile dsxFile = new MapVisitor().visitDsxFile(parser.dsxFile());
System.out.println(dsxFile);
}
}
class MapVisitor extends dsxGrammarBaseVisitor<Object> {
// dsxFile : headerDeclaration? jobDeclaration* EOF;
#Override
public DsxFile visitDsxFile(dsxGrammarParser.DsxFileContext ctx) {
Map<String, String> headerDeclaration = visitHeaderDeclaration(ctx.headerDeclaration());
List<JobDeclaration> jobDeclarations = new ArrayList<>();
if (ctx.jobDeclaration() != null) {
for (dsxGrammarParser.JobDeclarationContext child : ctx.jobDeclaration()) {
jobDeclarations.add(visitJobDeclaration(child));
}
}
return new DsxFile(headerDeclaration, jobDeclarations);
}
// headerDeclaration : BEGIN HEADER paramHeaders=keyValues END HEADER;
#Override
public Map<String, String> visitHeaderDeclaration(dsxGrammarParser.HeaderDeclarationContext ctx) {
return visitKeyValues(ctx.paramHeaders);
}
// jobDeclaration : BEGIN DSJOB paramJob+=keyValues recordDeclaration* paramJob+=keyValues END DSJOB;
#Override
public JobDeclaration visitJobDeclaration(dsxGrammarParser.JobDeclarationContext ctx) {
Map<String, String> paramJobs = new LinkedHashMap<>();
List<RecordDeclaration> recordDeclarations = new ArrayList<>();
for (dsxGrammarParser.KeyValuesContext child : ctx.paramJob) {
paramJobs.putAll(visitKeyValues(child));
}
for (dsxGrammarParser.RecordDeclarationContext child : ctx.recordDeclaration()) {
recordDeclarations.add(visitRecordDeclaration(child));
}
return new JobDeclaration(paramJobs, recordDeclarations);
}
// recordDeclaration : BEGIN DSRECORD ( paramRecord+=keyValue | subrecordDeclaration )* END DSRECORD;
#Override
public RecordDeclaration visitRecordDeclaration(dsxGrammarParser.RecordDeclarationContext ctx) {
Map<String, String> paramRecords = new LinkedHashMap<>();
List<Map<String, String>> subrecordDeclarations = new ArrayList<>();
for (dsxGrammarParser.KeyValueContext child : ctx.paramRecord) {
String[] keyValue = visitKeyValue(child);
paramRecords.put(keyValue[0], keyValue[1]);
}
for (dsxGrammarParser.SubrecordDeclarationContext child : ctx.subrecordDeclaration()) {
subrecordDeclarations.add(visitSubrecordDeclaration(child));
}
return new RecordDeclaration(paramRecords, subrecordDeclarations);
}
// subrecordDeclaration : BEGIN DSSUBRECORD paramSubRecords=keyValues END DSSUBRECORD;
#Override
public Map<String, String> visitSubrecordDeclaration(dsxGrammarParser.SubrecordDeclarationContext ctx) {
return visitKeyValues(ctx.paramSubRecords);
}
// keyValues : keyValue*;
#Override
public Map<String, String> visitKeyValues(dsxGrammarParser.KeyValuesContext ctx) {
Map<String, String> map = new LinkedHashMap<>();
if (ctx.keyValue() != null) {
for (dsxGrammarParser.KeyValueContext child : ctx.keyValue()) {
String[] keyValue = visitKeyValue(child);
map.put(keyValue[0], keyValue[1]);
}
}
return map;
}
// keyValue : PNAME PVALUE;
#Override
public String[] visitKeyValue(dsxGrammarParser.KeyValueContext ctx) {
return new String[] {
ctx.PNAME().getText(),
ctx.PVALUE().getText().substring(1, ctx.PVALUE().getText().length() - 1)
};
}
}
// headerDeclaration? jobDeclaration* EOF
class DsxFile {
final Map<String, String> headerDeclaration;
final List<JobDeclaration> jobDeclarations;
DsxFile(Map<String, String> headerDeclaration, List<JobDeclaration> jobDeclarations) {
this.headerDeclaration = headerDeclaration;
this.jobDeclarations = jobDeclarations;
}
#Override
public String toString() {
return "DsxFile{" +
"\n headerDeclaration=" + headerDeclaration +
"\n jobDeclarations=" + jobDeclarations +
"\n}";
}
}
// BEGIN DSJOB paramJob+=keyValues recordDeclaration* paramJob+=keyValues END DSJOB
class JobDeclaration {
final Map<String, String> paramJobs;
final List<RecordDeclaration> recordDeclarations;
JobDeclaration(Map<String, String> paramJobs, List<RecordDeclaration> recordDeclarations) {
this.paramJobs = paramJobs;
this.recordDeclarations = recordDeclarations;
}
#Override
public String toString() {
return "\n JobDeclaration{" +
"\n paramJobs=" + paramJobs +
"\n recordDeclarations=" + recordDeclarations +
"\n }";
}
}
// BEGIN DSRECORD ( paramRecord+=keyValue | subrecordDeclaration )* END DSRECORD
class RecordDeclaration {
final Map<String, String> paramRecords;
final List<Map<String, String>> subrecordDeclarations;
RecordDeclaration(Map<String, String> paramRecords, List<Map<String, String>> subrecordDeclarations) {
this.paramRecords = paramRecords;
this.subrecordDeclarations = subrecordDeclarations;
}
#Override
public String toString() {
return "\n RecordDeclaration{" +
"\n paramRecords=" + paramRecords +
"\n subrecordDeclarations=" + subrecordDeclarations +
"\n }";
}
}
If you run the main class, you'll see the following being printed to your console:
DsxFile{
headerDeclaration={CharacterSet=CP1252, ExportingTool=IBM InfoSphere DataStage Export, ToolVersion=8, ServerName=MIAIBV240, ToolInstanceID=DFDMGBL2, MDISVersion=1.0, Date=2018-02-12, Time=17.32.28, ServerVersion=8.7}
jobDeclarations=[
JobDeclaration{
paramJobs={Identifier=job_FDM_WVD_NET_SALES_DSC_STG_REPL_Load, DateModified=2018-02-12, TimeModified=17.32.24}
recordDeclarations=[
RecordDeclaration{
paramRecords={Identifier=C52, OLEType=CContainerStage, Readonly=0, Name=ShcFileAudit_Verif_Data, NextID=3}
subrecordDeclarations=[{Name=FDM_CMN, Description=#FDM_CMN#, ValueType=0}, {Name=FILE_NAME, JobVersion=56.0.0, ControlAfterSubr=0, Parameters=CParameters}]
}]
}]
}
Related
How to solve "The following sets of rules are mutually left-recursive" ANTLR error
The following sets of rules are mutually left-recursive [valeurBooleene, valeur] I know that ANTLR handle well left-recursive rules but only in one rule, not between rules. I have to rewrite my rules to solve the mutually left-recursive problem but I don't see how... grammar minimal; constante: bool = BOOLEAN | intgr = INTEGER ; valeurBooleene: '(' val = valeurBooleene ')' | bool = BOOLEAN | 'non' not = valeur | leftValue = valeur '=' rightValue = valeur ; valeur: '(' val = valeur ')' | cst = constante | booleanValue = valeurBooleene | '|' absolute = valeur '|' | '-' opposite = valeur ; condition: '(' cond = condition ')' | bool = valeurBooleene ; si: 'Si' cond = condition 'alors' ( thenActions += action )+ ( 'sinon' ( elseActions += action )+ )? ; system: ( actions += action )+ EOF ; action: ifClause = si ; BOOLEAN: 'false' | 'true' | 'faux' | 'vrai' ; INTEGER: [0-9]+ ; WS: [ \n\t\r]+ -> skip ;
Finally, I follow the advices provided by sepp2k like this: grammar minimal; value: '(' val = value ')' # Parenthesis | b = BOOLEAN # Boolean | 'non' not = value # Boolean | l = value '=' r = value # Boolean | i = INTEGER # Numeric | '|' abs = value '|' # Numeric | '-' opposite = value # Numeric ; condition: '(' condition ')' | cond = value ; ifStmnt: 'IF' condition 'THEN' ( actionsThen += action )+ ( 'ELSE' ( actionsElse += action )+ )? ; system: ( actions += action )+ EOF ; print: 'print' val = value ; action: ifs = ifStmnt | pr = print ; BOOLEAN: 'false' | 'true' ; INTEGER: [0-9]+ ; WS: [ \n\t\r]+ -> skip ; The labels create classes which extends ValueContext, I use them like this: package com.thalesgroup.dms.stimulus.factories; import minimal.minimalParser.BooleanContext; import minimal.minimalParser.ConditionContext; import minimal.minimalParser.NumericContext; import minimal.minimalParser.ParenthesisContext; import minimal.minimalParser.ValueContext; public class Sample { void analyze( ValueContext ctxt ) { if( ctxt instanceof ParenthesisContext ) { analyze(((ParenthesisContext)ctxt).val ); } else if( ctxt instanceof BooleanContext ) { final BooleanContext bc = (BooleanContext)ctxt; analyze( bc ); } else if( ctxt instanceof NumericContext ) { final NumericContext nc = (NumericContext)ctxt; analyze( nc ); } } void analyze( ConditionContext ctxt ) { if( ctxt.cond instanceof BooleanContext ) { final BooleanContext boolCtxt = (BooleanContext)ctxt.cond; // build a condition from a BooleanContext analyze( boolCtxt ); } else { throw new IllegalStateException(); } } void analyze( BooleanContext ctxt ) { if( ctxt.b != null ) { final String literal = ctxt.b.getText(); if( literal.equals( "true" )) { // deal with a boolean literal 'true' } else if( literal.equals( "false" )) { // deal with a boolean literal 'false' } } else if( ctxt.not != null ) { final BooleanContext bCtxt = (BooleanContext)ctxt.not; // deal with a 'not' operation } else if( ctxt.l != null ) { final ValueContext left = ctxt.l; final ValueContext right = ctxt.r; // deal with 'equals' } else { // ??? } } void analyze( NumericContext ctxt ) { if( ctxt.abs != null ) { // deal with abs( x ) } else if( ctxt.opposite != null ) { // deal with - x } else if( ctxt.i != null ) { // deal with literal integer } } }
Update Excel Cell using OLEDB
I want to update 1 cell in an Excel (xlsx) file using OLEDB. I have attached my code. The first time you run, it fills in the values because INSERT is working. The second time you run, it APPENDS the values because the UPDATE fails, and my Catch performs an INSERT. My goal is to have the UPDATE command work. When the UPDATE command executes, I get an error message: No value given for one or more required parameters. at System.Data.OleDb.OleDbCommand.ExecuteCommandTextErrorHandling(OleDbHResult hr) at System.Data.OleDb.OleDbCommand.ExecuteCommandTextForSingleResult(tagDBPARAMS dbParams, Object& executeResult) at System.Data.OleDb.OleDbCommand.ExecuteCommandText(Object& executeResult) at System.Data.OleDb.OleDbCommand.ExecuteCommand(CommandBehavior behavior, Object& executeResult) at System.Data.OleDb.OleDbCommand.ExecuteReaderInternal(CommandBehavior behavior, String method) at System.Data.OleDb.OleDbCommand.ExecuteNonQuery() at ConsoleApp3.Program.InsertCSVIntoSheet(String excelPath, String sheet, String csvPath) in C:\Users\jbendler.atlab\source\repos\ConsoleApp3\Program.cs:line 175 This code comes from a demo Console App. This is the program.cs file. The main part of the code is located at the bottom of the InsertCSVIntoSheet method. The output xlsx file is simply a new workbook, unedited. The input file is simply a text file, named with a .csv extension, that contains the following separated by carriage-return/linefeed - so the file has 5 lines with one character per line: ABCDE Thanks. using System; using System.Collections.Generic; using System.Data; using System.Data.OleDb; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Transactions; namespace ConsoleApp3 { class Program { static void Main(string[] args) { InsertCSVIntoSheet(#"c:\temp\book1.xlsx", "NewSheet", #"c:\temp\test.csv"); } private static void InsertCSVIntoSheet(string excelPath, string sheet, string csvPath) { int column; int row; int pos; bool done; char readUntilChar; string csvData; string columnName; string cell; string excelSheetName; List<Tuple<int, int, object>> values = new List<Tuple<int, int, object>>(); string connectionString = CreateOleDbConnectionStringForExcel(excelPath); OleDbCommand oleDbCommand = new OleDbCommand(); decimal decimalTest; DateTime dateTimeTest; int status; int numColumns; // Put CSV in to row/column/value Tuples using (StreamReader reader = new StreamReader(csvPath)) { csvData = reader.ReadToEnd(); row = 1; // Split the csv data by new line foreach (string line in csvData.Split(new string[] { "\r\n" }, StringSplitOptions.None)) { if (!string.IsNullOrEmpty(line)) { column = 1; pos = 0; cell = string.Empty; // Split each line by ',' to get each cell. A value wrapped in '"' can include a ',' while (pos < line.Length) { cell = string.Empty; // Check the first character. If it is a '"' then we assume the cell is surrounded // in '"' and do NOT include the '"' in the output to the excel cell. if (line[pos] == '"') { readUntilChar = '"'; pos++; } else { readUntilChar = ','; } done = line[pos] == readUntilChar; if (line[pos] == '"') { // Skip over second '"' for a blank ("") pos++; } while (!done) { cell += line[pos++]; if (pos == line.Length || line[pos] == readUntilChar) { if (readUntilChar == '"') { // Skip the '"' pos++; } done = true; } } // Skip over the ',' pos++; if (!string.IsNullOrEmpty(cell)) { // Test the data to determine the type (check for decimal and DateTime). if (decimal.TryParse(cell, out decimalTest)) { values.Add(new Tuple<int, int, object>(row, column, decimalTest)); } else if (DateTime.TryParse(cell, out dateTimeTest)) { values.Add(new Tuple<int, int, object>(row, column, dateTimeTest)); } else { // Write out the value as a string values.Add(new Tuple<int, int, object>(row, column, cell)); } } column++; } } row++; } } using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Suppress)) { excelSheetName = GetExcelSheetNames(connectionString).Where(n => n.ToUpper().StartsWith(sheet.ToUpper())).FirstOrDefault(); //Set the connection string to recognize the header and to operate in Update mode(IMEX= 0) using (OleDbConnection oleDbConnection = new OleDbConnection(connectionString.Replace("IMEX=1", "IMEX=0"))) { oleDbConnection.Open(); oleDbCommand = new OleDbCommand(); oleDbCommand.Connection = oleDbConnection; oleDbCommand.CommandType = CommandType.Text; if (excelSheetName != null) { // Delete Sheet oleDbCommand.CommandText = "DROP TABLE [" + sheet + "]"; status = oleDbCommand.ExecuteNonQuery(); } else { excelSheetName = sheet + "$"; } numColumns = values.Max(v => v.Item2); oleDbCommand.CommandText = "CREATE TABLE [" + sheet + "]("; for (int index = 0; index < numColumns; index++) { oleDbCommand.CommandText += "Column" + index.ToString() + " CHAR(255), "; } oleDbCommand.CommandText = oleDbCommand.CommandText.Substring(0, oleDbCommand.CommandText.Length - 2) + ")"; status = oleDbCommand.ExecuteNonQuery(); } using (OleDbConnection oleDbConnection = new OleDbConnection(connectionString.Replace("IMEX=1", "IMEX=0"))) { oleDbConnection.Open(); oleDbCommand.Connection = oleDbConnection; // Write out new values foreach (Tuple<int, int, object> tuple in values) { try { columnName = GetExcelColumnName(tuple.Item2) + (tuple.Item1 + 1).ToString(); oleDbCommand.CommandText = "UPDATE [" + excelSheetName + columnName + ":" + columnName + "] SET " + "F1" + " = '" + tuple.Item3.ToString() + "'"; status = oleDbCommand.ExecuteNonQuery(); } catch (OleDbException oledbex) { oleDbCommand.CommandText = "INSERT INTO [" + excelSheetName + "] VALUES ('" + tuple.Item3.ToString() + "')"; status = oleDbCommand.ExecuteNonQuery(); } } } } } private static List<string> GetExcelSheetNames(string connectionString) { OleDbConnection oleDbConnection = null; DataTable dataTable = null; List<string> excelSheetNames = null; using (oleDbConnection = new OleDbConnection(connectionString)) { oleDbConnection.Open(); dataTable = oleDbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); } if (dataTable != null) { excelSheetNames = new List<string>(dataTable.Rows.Cast<DataRow>().Where(r => r["TABLE_NAME"].ToString().EndsWith("$") || r["TABLE_NAME"].ToString().EndsWith("$'")).Select(r => r["TABLE_NAME"].ToString().ToUpper())); } return excelSheetNames; } private static string CreateOleDbConnectionStringForExcel(string sourceFile) { var fileInfo = new FileInfo(sourceFile); switch (fileInfo.Extension.ToUpper()) { case ".XLS": return string.Format("Provider=Microsoft.ACE.OLEDB.12.0;;Data Source='{0}';Extended Properties='Excel 8.0;HDR=No;IMEX=1'", sourceFile); case ".XLSX": case ".XLSM": return string.Format("Provider=Microsoft.ACE.OLEDB.12.0;;Data Source='{0}';Extended Properties='Excel 12.0;HDR=No;IMEX=1'", sourceFile); default: throw new NotSupportedException("File type not supported."); } } private static string GetExcelColumnName(int columnNumber) { string columnName = String.Empty; int dividend = columnNumber; int modulo; while (dividend > 0) { modulo = (dividend - 1) % 26; columnName = Convert.ToChar(65 + modulo).ToString() + columnName; dividend = (int)((dividend - modulo) / 26); } return columnName; } } }
ANTLR4: Expected ID got ID
Why ANTLR don't recognize the tokens correctly? I think that the problem is something to do with the "MemberAccessor" has an ID and the "CustomMethodCall" rule too. Grammar **statement : varDeclaration | customMethodCall; customMethodCall : (memberAccessor '.)? ID L_PAREN expressionList R_PAREN; memberAcessorPart : ID | 'this' | ID '.getAt' L_PAREN expression R_PAREN | ID arrayGet: | ID L_PAREN expressionList R_PAREN; memberAccessor : memberAcessorPart (ACESSAR memberAcessorPart)*; ** protected override void ReportNoViableAlternative (Parser recognizer, NoViableAltException e) { string[] expected = new String[e.GetExpectedTokens ().Count]; int[] expectedInts = e.GetExpectedTokens ().ToArray (); for (int i = 0; i < expected.Length; i++) { if (expectedInts[i] == -1) expected[i] = "<EOF>"; else expected[i] = PortuProParser.DefaultVocabulary.GetDisplayName (expectedInts[i]); } string c = PortuProParser.DefaultVocabulary.GetDisplayName (e.StartToken.Type); if (expected.ToList ().IndexOf (c) == -1) throw new PortuProException (e.StartToken.Line, e.StartToken.Column, "Entrada desconhecida. '" + e.StartToken.Text + "'\n\nEsperado: '" + string.Join (", ", expected) + "'"); } Well i've got on 'c' the 'ID' and the 'ID' is expected as token.
Spark combineByKey over reduceBykey using Java
I have the below dataset: key value --------------------------- key1,CLASS-A,YES,1 key2,CLASS-B,YES,2 key2,CLASS-B,YES,1 key1,CLASS-A,YES,4 key3,CLASS-C,DEFAULT,1 OUTPUT should look like: key value --------------------------- key1, CLASS-A,YES,5 key2, CLASS-B,YES,3 key3, CLASS-C,NO,1 While using reduceByKey to obtain the result , i found that whenever there is a key with only one value , in this case key3, the reduceByKey is not called as there is nothing to reduce against.And I get : key value --------------------------- key1, CLASS-A,YES,5 key2, CLASS-B,YES,3 key3, CLASS-C,DEAFULT,1 Can I achieve this using combineByKey in Spark (Java) . What I tried so far : reduceByKey( new Function2<String, String, String>() { #Override public String call(String s1, String s2) throws Exception { String[] vals1 = StringUtils.split(s1, ","); String[] vals2 = StringUtils.split(s2, ","); String jobStat1 = vals1[0]; String jobStat2 = vals2[0]; String reducedJobStat; boolean s1 = jobStat1.equals("YES") boolean s2 = jobStat2.equals("YES"); if (s1 || s2) { reducedJobStat = "YES"; } else { reducedJobStat = "NO"; } return reducedJobStat; } } )
the fundamental difference between reduceByKey and combineByKey in spark is that reduceByKey requires a function that takes a pair of values and returns a single value, whereas combineByKey allows for you to simultaneously transform your data and it requires three functions. The first is to create the new value type from the existing value type, the second adds an existing value type to a new value type, and the third adds to of the new value type. The best example I have seen of combineByKey is at http://codingjunkie.net/spark-combine-by-key/ For your specific case, I'd recommend keeping it simple and using reduceByKey followed by mapValues to accomplish the desired transformation on key3. This might look something like: reduced_rdd.mapValues(v => (v._1, if (v._2 == "DEFAULT") "NO" else v._2, v._3))
So , I got an alternate solution using combinerByKey. The code for reduceByKey looks simpler, but I am doing a mapValues after the reduceByKey(please look the question for reason) to get the result . CombineByKey is fairly simple if we understand how it internally works . Example using CombineByKey Input : key value key1,CLASS-A,YES,1 key2,CLASS-B,YES,2 key2,CLASS-B,YES,1 key1,CLASS-A,YES,4 key3,CLASS-C,DEFAULT,1 //The CreateCombiner will initialise the 1st Key in the 1st partition . Here Lets divide the input into 2 partitions: Partition 1: Partition 2: key value key value --------------------------- --------------------------- key1, CLASS-A,YES,1 key1, CLASS-A,YES,4 key2, CLASS-B,YES,2 key3, CLASS-C,DEFAULT,1 key2, CLASS-B,YES,1 public class CreateCombiner implements Function<String, String> { #Override public String call(String value) { //value here is "CLASS-A,YES,1" String jobStatus = value.split(",")[0]; if (jobStatus.equals("YES") || jobStatus.equals("DEFAULT") { return "YES"+ " " + value.split(" ")[1] + " " + value.split(" ")[2]; } else { return "NO" + " " + value.split(" ")[1] + " " + value.split(" ")[2]; } } } When the Key1 in 1st partition is encounterd, the CreateCombiner will initialise that key's. (key1 here) value,In our case we change the value(2nd string(YES/NO/DEFAULT)). Becase in my usecase I want to change all "DEFAULT" to "YES" . It replaces all the YES and DEFAULT strings to YES and otherwise to NO. Now Same for Key2 in the 1st partition . Again when it finds key2 in the 1st partition , the MergeValue class is called. It will merge the values . So here Key2 has 2 values(CLASS-B,YES,2 and CLASS-B,YES,1). It merges both. like (key2,CLASS-B,YES,3) The MergeCombiner takes the combiners (tuples) created on each partition and merges them together. So in my case the logic is same as in MergeValue. public class MergeValue implements Function2<String, String, String> { // MergeCombiner will decide the jobStatus and add the outCount and lbdCount. // This is a Merging function that takes a value and merges it into the previously collecte value(s). #Override public String call(String v1, String v2) throws Exception { String[] vals1 = StringUtils.split(v1, ","); String[] vals2 = StringUtils.split(v2, ","); String jobStat1 = vals1[0]; String jobStat2 = vals2[0]; String reducedJobStat; boolean stat1Process = (jobStat1.equals("YES")) || (jobStat1.equals("DEFAULT")); boolean stat2Process = (jobStat2.equals("YES")) || (jobStat2.equals("DEFAULT")); if (stat1Process || stat2Process) { reducedJobStat = "YES"; } else { reducedJobStat = "NO"; } int outCount = Integer.parseInt(vals1[1]) + Integer.parseInt(vals2[1]); int lbdCount = Integer.parseInt(vals1[2]) + Integer.parseInt(vals2[2]); return reducedJobStat + " " + Integer.toString(outCount) + " " + Integer.toString(lbdCount); } } public class MergeCombiner implements Function2<String, String, String> { // This fucntion combines the merged values together from MergeValue. // Basically this function takes the new values produced at the partition level and combines them until we end up // with one singular value. #Override public String call(String v1, String v2) throws Exception { String[] vals1 = StringUtils.split(v1, ","); String[] vals2 = StringUtils.split(v2, ","); String jobStat1 = vals1[0]; String jobStat2 = vals2[0]; String reducedJobStat; //Here we decide the jobStatus from 2 combiners , if both of them are complete ie jobStat1 and jobStat2 is COMP // LETE, then the Status is marked as complete. boolean stat1Process = (jobStat1.equals("YES"); boolean stat2Process = (jobStat2.equals("YES"); if (stat1Process || stat2Process) { reducedJobStat = "YES"; } else { reducedJobStat = "YES"; } int outCount = Integer.parseInt(vals1[1]) + Integer.parseInt(vals2[1]); int lbdCount = Integer.parseInt(vals1[2]) + Integer.parseInt(vals2[2]); return reducedJobStat + " " + Integer.toString(outCount) + " " + Integer.toString(lbdCount); } Call to combineByKey combineByKey(new CreateCombiner(), new MergeValue(), new MergeCombiner()); The same code Inmplemented using reduceByKey: reduceByKey( new Function2<String, String, String>() { #Override public String call(String s1, String s2) throws Exception { String[] vals1 = StringUtils.split(s1, " "); String[] vals2 = StringUtils.split(s2, " "); String jobStat1 = vals1[0]; String jobStat2 = vals2[0]; String reducedJobStat; boolean stat1Process = (jobStat1.equals("YES")) || (jobStat1.equals("DEFAULT"); boolean stat2Process = (jobStat2.equals("YES")) || (jobStat2.equals("DEFAULT"); if (stat1Process || stat2Process) { reducedJobStat = "YES"; } else { reducedJobStat = "NO"; } int outCount = Integer.parseInt(vals1[1]) + Integer.parseInt(vals2[1]); int lbdCount = Integer.parseInt(vals1[2]) + Integer.parseInt(vals2[2]); return reducedJobStat + " " + Integer.toString(outCount) + " " + Integer.toString(lbdCount); } } ).mapValues(new Function<String, String>() { #Override public String call(String s) throws Exception { String jobStatus = s.split(" ")[0]; if (jobStatus.equals("YES") || jobStatus.equals("DEFAULT") { return "YES" + " " + s.split(" ")[1] + " " + s.split(" ")[2]; } else { return "NO" + " " + s.split(" ")[1] + " " + s.split(" ")[2]; } } });
Subsonic 3.0 missing output parameter/default parameter support in SQL Server?
I have a stored proc: CREATE PROCEDURE MyProc ( #P1 uniqueidentifier, #P2 int = NULL output, #P3 int = NULL output ) The signature on the C# side should be (Guid P1, int? P2, int? P3) I would think. But it's not, it's just (Guid,int,int). Furthermore, the output params are not created as output params, they're just straight "AddParameter(P2,arg,DbType.Int32)". Was this meant to work?
Well, it doesn't look like the native T4 templates handle this so well. We had actually built something more complex back in the VS2003 days, so I've updated it for Subsonic. The idea was that each stored proc gets a class instead of a function and has nullable properties for parameters. I've modified several templates, I'm sure I'm missing something here and this is not "production quality", but here's StoredProcedures.tt <## template language="C#v3.5" debug="False" hostspecific="True" #> <## output extension=".cs" #> <## include file="SQLServer.ttinclude" #> <# var sps = GetSPs(); if(sps.Count>0){ #> using System; using SubSonic; using SubSonic.Schema; using SubSonic.DataProviders; using System.Data; namespace <#=Namespace#>{ public partial class <#=DatabaseName#>DB{ <# foreach(var sp in sps){#> public class <#=sp.CleanName#> : StoredProcedure { public <#=sp.CleanName#>(<#=DatabaseName#>DB database, <#=sp.ArgList#>) : base("<#=sp.Name#>",database.Provider) { <# foreach(var par in sp.Parameters){ if (par.IsOutput) { #> Command.AddOutputParameter("<#=par.Name#>",DbType.<#=par.DbType#>); <# } else { #> Command.AddParameter("<#=par.Name#>",<#=par.CleanName#>,DbType.<#=par.DbType#>); <# } }#> } <# foreach (var par in sp.Parameters) { #> public <#= par.SysType #><#= par.ShouldBeNullable ? "?" : "" #> <#= par.CleanName #> { get { object val = Command.Parameters.GetParameter("<#= par.Name #>").ParameterValue; return val == DBNull.Value ? default(<#= par.SysType #><#= par.ShouldBeNullable ? "?" : "" #>) : (<#= par.SysType #><#= par.ShouldBeNullable ? "?" : "" #>)val; } set { <# if (par.ShouldBeNullable) { #> object val = value.HasValue ? (object)value : (object)DBNull.Value; Command.Parameters.GetParameter("<#= par.Name #>").ParameterValue = val; <# } #> } } <# } #> } <# } #> } } <# }#> And here's the GetSPParams function from SQLServer.ttinclude List<SPParam> GetSPParams(string spName){ var result=new List<SPParam>(); string[] restrictions = new string[4] { DatabaseName, null, spName, null }; using(SqlConnection conn=new SqlConnection(ConnectionString)){ conn.Open(); var sprocs=conn.GetSchema("ProcedureParameters", restrictions); conn.Close(); foreach(DataRow row in sprocs.Select("", "ORDINAL_POSITION")){ SPParam p=new SPParam(); p.SysType=GetSysType(row["DATA_TYPE"].ToString()); p.DbType=GetDbType(row["DATA_TYPE"].ToString()).ToString(); p.Name=row["PARAMETER_NAME"].ToString().Replace("#",""); p.IsOutput=(row["PARAMETER_MODE"].ToString() == "INOUT"); p.CleanName=CleanUp(p.Name); result.Add(p); } } return result; } And then the class declaration from Settings.ttinclude public class SP{ public string Name; public string CleanName; public string ClassName; public List<SPParam> Parameters; public SP(){ Parameters=new List<SPParam>(); } public string ArgList{ get{ StringBuilder sb=new StringBuilder(); foreach(var par in Parameters){ if (par.ShouldBeNullable) { continue; } if(sb.Length != 0) { sb.Append(", "); } sb.AppendFormat("{0}{1} {2}", par.SysType, par.ShouldBeNullable ? "?" : "", par.CleanName); } return sb.ToString(); } } } public class SPParam{ public string Name; public string CleanName; public string SysType; public string DbType; public bool IsOutput; public bool ShouldBeNullable { get { return IsOutput && (SysType == "int" || SysType == "bool" || SysType == "double" || SysType == "long" || SysType == "short" || SysType == "decimal"); } } }