Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.expressionevaluator / org.gvsig.expressionevaluator.lib / org.gvsig.expressionevaluator.lib.api / src / main / java / org / gvsig / expressionevaluator / spi / AbstractLexicalAnalyzer.java @ 44421

History | View | Annotate | Download (15.3 KB)

1
package org.gvsig.expressionevaluator.spi;
2

    
3
import org.gvsig.expressionevaluator.LexicalAnalyzer;
4
import java.text.NumberFormat;
5
import java.text.ParsePosition;
6
import java.util.HashMap;
7
import java.util.Locale;
8
import java.util.Map;
9
import java.util.Objects;
10
import java.util.Stack;
11
import org.apache.commons.lang3.StringUtils;
12
import org.gvsig.expressionevaluator.ExpressionRuntimeException;
13
import org.gvsig.expressionevaluator.ExpressionSyntaxException;
14
import org.gvsig.expressionevaluator.I18N;
15
import org.gvsig.tools.lang.Cloneable;
16

    
17
public abstract class AbstractLexicalAnalyzer implements LexicalAnalyzer {
18

    
19
    protected class DefaultToken implements Token {
20

    
21
        private int type;
22
        private String literal;
23
        private Object value;
24

    
25
        public DefaultToken() {
26
        }
27

    
28
        @Override
29
        public Token clone() throws CloneNotSupportedException {
30
            // We will assume that the properties of the class are immutable, so 
31
            // it would suffice to call the super class.
32
            DefaultToken other = (DefaultToken) super.clone();
33
            return other;
34
        }
35

    
36
        @Override
37
        public void set(int type, String literal) {
38
            this.set(type, literal, literal);
39
        }
40

    
41
        @Override
42
        public void set(int type, String literal, Object value) {
43
            this.literal = literal;
44
            this.type = type;
45
            this.value = value;
46
        }
47

    
48
        @Override
49
        public int getType() {
50
            return type;
51
        }
52

    
53
        @Override
54
        public Object getValue() {
55
            return value;
56
        }
57

    
58
        @Override
59
        public String getLiteral() {
60
            return literal;
61
        }
62

    
63
        @Override
64
        public void setLiteral(String literal) {
65
            this.literal = literal;
66
        }
67

    
68
        @Override
69
        public boolean is(String... values) {
70
            for (String theValue : values) {
71
                if( StringUtils.isBlank(literal) ) {
72
                    if( StringUtils.isBlank(theValue) ) {
73
                        return true;
74
                    }
75
                    continue;
76
                }
77
                if( StringUtils.isBlank(theValue) ) {
78
                    continue;
79
                }
80
                if( theValue.trim().equalsIgnoreCase(this.literal.trim()) ) {
81
                    return true;
82
                }
83
            }
84
            return false;
85
        }
86

    
87
        @Override
88
        public String toString() {
89
            return String.format("{%d,%s,%s}", this.type, Objects.toString(this.literal), Objects.toString(value));
90
        }
91
        
92
    }
93

    
94
    protected class Buffer implements Cloneable {
95

    
96
        StringBuilder builder;
97

    
98
        public Buffer() {
99
            this.builder = new StringBuilder();
100
        }
101

    
102
        @Override
103
        public Buffer clone() throws CloneNotSupportedException {
104
            Buffer other = (Buffer) super.clone();
105
            other.builder = new StringBuilder(builder);
106
            return other;
107
        }
108

    
109
        public void clear() {
110
            builder.delete(0, builder.length());
111
        }
112

    
113
        public void add(char ch) {
114
            builder.append(ch);
115
        }
116

    
117
        public int length() {
118
            return this.builder.length();
119
        }
120

    
121
        @Override
122
        public String toString() {
123
            return this.builder.toString();
124
        }
125
    }
126

    
127
    protected static final char EOF = 0;
128

    
129
    private NumberFormat nf;
130
    private ParsePosition nfPos;
131
    private Stack<Integer> states;
132
    private String source;
133
    private int position;
134
    private int lineno;
135
    private int column;
136

    
137
    protected Buffer buffer;
138
    protected Token token;
139
    protected Map<String, Integer> tokens;
140
    protected boolean useBracketsForIdentifiers;
141
            
142
    public AbstractLexicalAnalyzer(String source) {
143
        this.useBracketsForIdentifiers = false;
144
        this.position = 0;
145
        this.source = source;
146
        this.states = new Stack<>();
147
        this.buffer = new Buffer();
148
        this.token = this.createToken();
149

    
150
        this.nf = NumberFormat.getInstance(Locale.UK);
151
        this.nf.setGroupingUsed(false);
152
        
153
        this.nfPos = new ParsePosition(0);
154

    
155
        this.tokens = new HashMap<>();
156
    }
157

    
158
    public AbstractLexicalAnalyzer() {
159
        this(null);
160
    }
161

    
162
    protected Token createToken() {
163
        return new DefaultToken();
164
    }
165
    
166
    @Override
167
    public LexicalAnalyzer clone() throws CloneNotSupportedException {
168
        AbstractLexicalAnalyzer other = (AbstractLexicalAnalyzer) super.clone();
169
        other.nf = NumberFormat.getInstance(Locale.UK);
170
        other.nfPos = new ParsePosition(0);
171
        other.buffer = buffer.clone();
172
        other.token = token.clone();
173
        other.states = new Stack<>();
174
        other.states.addAll(states);
175
        other.tokens = new HashMap<>(tokens);
176
        return other;
177
    }
178

    
179
    @Override
180
    public void setSource(String source) {
181
        this.source = source;
182
        this.position = 0;
183
    }
184

    
185
    @Override
186
    public String getSource() {
187
        return this.source;
188
    }
189

    
190
    @Override
191
    public Token next() {
192
        return getToken();
193
    }
194

    
195
    @Override
196
    public Token look() {
197
        push_state();
198
        try {
199
            return getToken();
200
        } finally {
201
            pop_state();
202
        }
203
    }
204

    
205
    abstract protected Token getToken();
206

    
207
    protected void push_state() {
208
        this.states.push(position);
209
    }
210

    
211
    protected void pop_state() {
212
        position = this.states.pop();
213
    }
214

    
215
    @Override
216
    public int getPosition() {
217
        return position;
218
    }
219
    
220
    private void calcualteLineAndColumn() {
221
        final String s = this.source;
222
        int max = s.length();
223
        if( max > this.position ) {
224
            max = this.position;
225
        }
226
        int line = 1;
227
        int col = 0;
228
        for (int i = 0; i < max ; i++) {
229
            if( s.charAt(i)=='\n' ) {
230
                line++;
231
                col = 0;
232
            } 
233
            col++;
234
        }
235
        this.lineno = line;
236
        this.column = col;
237
    }
238

    
239
    @Override
240
    public int getLine() {
241
        this.calcualteLineAndColumn();
242
        return lineno;
243
    }
244

    
245
    @Override
246
    public int getColumn() {
247
        this.calcualteLineAndColumn();
248
        return column;
249
    }
250

    
251
    @Override
252
    public boolean isEOF() {
253
        return this.position >= this.source.length();
254
    }
255

    
256
    protected void skipblanks() {
257
        if (isEOF()) {
258
            return;
259
        }
260
        char ch = getch();
261
        while (ch != EOF && Character.isWhitespace(ch)) {
262
            ch = getch();
263
        }
264
        ungetch();
265
    }
266

    
267
    protected char lookch() {
268
        if (this.position >= this.source.length()) {
269
            return EOF;
270
        }
271
        return this.source.charAt(this.position);
272
    }
273

    
274
    protected char getch() {
275
        if (this.position >= this.source.length()) {
276
            return EOF;
277
        }
278
        this.column++;
279
        return this.source.charAt(this.position++);
280
    }
281

    
282
    protected void ungetch() {
283
        this.position--;
284
        if (this.position < 0) {
285
            this.position = 0;
286
        }
287
        this.column--;
288
        if (this.column < 0) {
289
            this.column = 0;
290
        }
291
    }
292

    
293
    protected void parseString() {
294
        buffer.clear();
295
        char ch = getch();
296
        while (true) {
297
            if (ch == EOF) {
298
                throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);
299
            }
300
            if (ch == '\'') {
301
                ch = getch();
302
                if (ch == EOF) {
303
                    break;
304
                }
305
                if (ch != '\'') {
306
                    ungetch();
307
                    break;
308
                }
309
            }
310
            buffer.add(ch);
311
            ch = getch();
312
        }
313
        token.set(Token.STRING_LITERAL, buffer.toString());
314
    }
315

    
316
    protected void parseSpecialNumber() {
317
        char ch = getch();
318
        if( ch!='@' ) {
319
            throw new ExpressionSyntaxException(I18N.Wrong_special_number_start(), this);
320
        }
321
        buffer.clear();
322
        ch = getch();
323
        while (true) {
324
            if (ch == EOF) {
325
                throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);
326
            }
327
            if( !Character.isAlphabetic(ch) ) {
328
                break;
329
            }
330
            buffer.add(ch);
331
            ch = getch();
332
        }
333
        String formatName = buffer.toString();
334
        if( StringUtils.isBlank(formatName) ) {
335
            formatName = "DMS";
336
        }
337
        formatName = formatName.toUpperCase();
338
        /*
339
        From https://es.wikipedia.org/wiki/Sistema_de_coordenadas#Coordenadas_geogr%C3%A1ficas
340
        
341
        DD --- Decimal Degree (Grados Polares)
342
        DM --- Degree:Minute (Grados:Minutos)
343
        DMS -- Degree:Minute:Second 
344
        */
345
        switch(formatName) {
346
            case "DMS":
347
                ungetch();
348
                parseDMSNumber();
349
                break;
350
            default:
351
                throw new ExpressionSyntaxException(I18N.Special_number_format_not_supported(formatName), this);
352
        }
353
    }
354

    
355
    private void parseDMSNumber() {
356
        int d;
357
        int m;
358
        int s_i;
359
        int s_d;
360
        double s;
361
        String ll;
362
        char ch;
363
        
364
        // Parseamos los grados
365
        skipblanks();
366
        ch = getch();
367
        buffer.clear();
368
        if( !Character.isDigit(ch) ) {
369
            throw new ExpressionSyntaxException(I18N.Expected_a_number_at_position_XpositionX(this.getPosition()), this);        
370
        }
371
        while (true) {
372
            if (ch == EOF) {
373
                throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);        
374
            }
375
            if( !Character.isDigit(ch) ) {
376
                break;
377
            }
378
            buffer.add(ch);
379
            ch = getch();
380
        }
381
        if( !StringUtils.contains(" ?:", ch) ) {
382
            throw new ExpressionSyntaxException(I18N.Expected_XexpectedX_and_found_XfoundX(" ", String.valueOf(ch)), this);
383
        }
384
        d = Integer.parseInt(buffer.toString());
385
        
386
        // Parseamos los minutos
387
        skipblanks();
388
        ch = getch();
389
        buffer.clear();
390
        if( !Character.isDigit(ch) ) {
391
            throw new ExpressionSyntaxException(I18N.Expected_a_number_at_position_XpositionX(this.getPosition()), this);        
392
        }
393
        while (true) {
394
            if (ch == EOF) {
395
                throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);        
396
            }
397
            if( !Character.isDigit(ch) ) {
398
                break;
399
            }
400
            buffer.add(ch);
401
            ch = getch();
402
        }
403
        if( !StringUtils.contains(" ':", ch) ) {
404
            throw new ExpressionSyntaxException(I18N.Expected_XexpectedX_and_found_XfoundX(" ", String.valueOf(ch)), this);
405
        }
406
        m = Integer.parseInt(buffer.toString());
407

    
408
        // Parseamos la parte entera de los segundos
409
        skipblanks();
410
        ch = getch();
411
        buffer.clear();
412
        if( !Character.isDigit(ch) ) {
413
            throw new ExpressionSyntaxException(I18N.Expected_a_number_at_position_XpositionX(this.getPosition()), this);        
414
        }
415
        while (true) {
416
            if (ch == EOF) {
417
                throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);        
418
            }
419
            if( !Character.isDigit(ch) ) {
420
                break;
421
            }
422
            buffer.add(ch);
423
            ch = getch();
424
        }
425
        s_i = Integer.parseInt(buffer.toString());
426

    
427
        if( ch == '.' ) {
428
            // Parseamos la parte decimal de los segundos
429
            skipblanks();
430
            ch = getch();
431
            buffer.clear();
432
            if( !Character.isDigit(ch) ) {
433
                throw new ExpressionSyntaxException(I18N.Expected_a_number_at_position_XpositionX(this.getPosition()), this);        
434
            }
435
            while (true) {
436
                if (ch == EOF) {
437
                    throw new ExpressionSyntaxException(I18N.End_of_string_was_expected_and_end_of_source_was_found(), this);        
438
                }
439
                if( !Character.isDigit(ch) ) {
440
                    break;
441
                }
442
                buffer.add(ch);
443
                ch = getch();
444
            }            
445
            s_d = Integer.parseInt(buffer.toString());
446
        } else {
447
            s_d = 0;
448
        }
449
        if( !StringUtils.contains(" \"", ch) ) {
450
            throw new ExpressionSyntaxException(I18N.Expected_XexpectedX_and_found_XfoundX(" ", String.valueOf(ch)), this);
451
        }
452
        
453
        s = Double.parseDouble(s_i+"."+s_d);
454
        
455
        double dd = d + m / 60.0 + s / 3600.0;
456
        
457
        skipblanks();
458
        ch = getch();
459
        switch(ch) {
460
            case 'N':
461
            case 'n':
462
                if( dd>90 ) {
463
                    throw new ExpressionSyntaxException(I18N.Incorrect_value_for_latitude(dd), this);
464
                }
465
                dd = -dd;
466
                break;
467
                
468
            case 'S':
469
            case 's':
470
                if( dd>90 ) {
471
                    throw new ExpressionSyntaxException(I18N.Incorrect_value_for_latitude(dd), this);
472
                }
473
                break;
474
            case 'E':
475
            case 'e':
476
                if( dd>180 ) {
477
                    throw new ExpressionSyntaxException(I18N.Incorrect_value_for_latitude(dd), this);
478
                }
479
                break;
480
                
481
            case 'O':
482
            case 'o':
483
            case 'W':
484
            case 'w':
485
                if( dd>180 ) {
486
                    throw new ExpressionSyntaxException(I18N.Incorrect_value_for_latitude(dd), this);
487
                }
488
                dd = -dd;
489
                break;
490
                
491
            default:
492
                throw new ExpressionSyntaxException(I18N.Expected_XexpectedX_and_found_XfoundX("N/S/E/W", String.valueOf(ch)), this);
493
        }
494
        ll = String.valueOf(Character.toUpperCase(ch));
495
        
496
        token.set(
497
                Token.FLOATING_POINT_LITERAL,
498
                String.format("@%d? %d' %f\" %s", d,m,s,ll) ,
499
                dd
500
        );
501
        
502
    }
503
    
504
    protected void parseNumber() {
505
        this.nfPos.setIndex(this.position);
506
        Number n = nf.parse(source, this.nfPos);
507
        if (this.nfPos.getIndex() == this.position) {
508
            throw new ExpressionRuntimeException(I18N.Expected_a_number_at_position_XpositionX(this.nfPos.getIndex()));
509
        }
510
        String literal = source.substring(this.position, this.nfPos.getIndex());
511
        this.position = this.nfPos.getIndex();
512
        if( n instanceof Long ) {
513
            long l = ((Long)n);
514
            if( l>Integer.MIN_VALUE && l<Integer.MAX_VALUE ) {
515
                token.set(Token.INTEGER_LITERAL, literal, (int)l);
516
            } else {
517
                token.set(Token.INTEGER_LITERAL, literal, n);
518
            }
519
        } else if( n instanceof Integer) {
520
            token.set(Token.INTEGER_LITERAL, literal, n);
521
        } else {
522
            token.set(Token.FLOATING_POINT_LITERAL, literal, n);
523
        }
524
    }
525
    
526
    @Override
527
    public void setUseBracketsForIdentifiers(boolean useBracketsForIdentifiers) {
528
        this.useBracketsForIdentifiers = useBracketsForIdentifiers;
529
    }
530
    
531
    @Override
532
    public boolean getUseBracketsForIdentifiers() {
533
        return this.useBracketsForIdentifiers;
534
    }
535
}