Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
37330 amit 1
package com.smartdukaan.cron.offercircular;
2
 
3
import java.util.ArrayList;
4
import java.util.LinkedHashSet;
5
import java.util.List;
6
import java.util.Set;
7
import java.util.regex.Matcher;
8
import java.util.regex.Pattern;
9
 
10
/**
11
 * Parses the "EMI tenure" cell into (months, scheme) pairs.
12
 *
13
 * The scheme marker always FOLLOWS the group of months it applies to, so months are
14
 * accumulated and flushed each time a marker is seen:
15
 *   "3,6 NC 9,12,18,24 LC" -> 3/NCE, 6/NCE, 9/LCE, 12/LCE, 18/LCE, 24/LCE
16
 *
17
 * Some cells name a payment mode rather than a tenure ("Full Swipe", "UPI", "CIB"
18
 * alone) and correctly yield nothing.
19
 */
20
public final class TenureParser {
21
 
22
    /** Tenures the OEMs actually offer; anything else is reported, not stored. */
23
    private static final Set<Integer> VALID_MONTHS = new LinkedHashSet<>(
24
            java.util.Arrays.asList(3, 6, 9, 12, 18, 24));
25
 
26
    private static final Pattern TOKEN = Pattern.compile("\\d+|[A-Z]+");
27
    private static final Pattern HAS_DIGIT = Pattern.compile("\\d");
28
    private static final Pattern MODE_ONLY = Pattern.compile("FULL SWIPE|UPI|CIB");
29
 
30
    public static final class Tenure {
31
        private final int months;
32
        private final String scheme;
33
        Tenure(int months, String scheme) { this.months = months; this.scheme = scheme; }
34
        public int getMonths() { return months; }
35
        public String getScheme() { return scheme; }
36
        @Override public boolean equals(Object o) {
37
            if (!(o instanceof Tenure)) { return false; }
38
            Tenure t = (Tenure) o;
39
            return months == t.months && scheme.equals(t.scheme);
40
        }
41
        @Override public int hashCode() { return months * 31 + scheme.hashCode(); }
42
        @Override public String toString() { return months + ":" + scheme; }
43
    }
44
 
45
    public static final class Result {
46
        private final List<Tenure> tenures = new ArrayList<>();
47
        private String warning;
48
        public List<Tenure> getTenures() { return tenures; }
49
        public String getWarning() { return warning; }
50
    }
51
 
52
    private TenureParser() { }
53
 
54
    private static String scheme(String token) {
55
        switch (token) {
56
            case "NCE": case "NC": return "NCE";
57
            case "LCE": case "LC": return "LCE";
58
            case "CIB":            return "CIB";
59
            default:               return null;
60
        }
61
    }
62
 
63
    public static Result parse(String cell) {
64
        Result result = new Result();
65
        if (cell == null) {
66
            return result;
67
        }
68
        String text = cell.trim();
69
        if (text.isEmpty() || "-".equals(text) || "NA".equals(text) || "N/A".equals(text)) {
70
            return result;
71
        }
72
        // PDF line-wrap artefacts: "(L CE)" and "1 8" (meaning 18) are one token split
73
        // across a line break and rejoined with a space.
74
        text = text.replaceAll("(?i)\\bL\\s+CE\\b", "LCE")
75
                   .replaceAll("(?i)\\bN\\s+CE\\b", "NCE")
76
                   .replaceAll("(?<=\\d)\\s+(?=\\d)", "");
77
        String upper = text.toUpperCase();
78
 
79
        if (!HAS_DIGIT.matcher(upper).find()) {
80
            result.warning = MODE_ONLY.matcher(upper).find() ? "mode-only" : "no-digits";
81
            return result;
82
        }
83
 
84
        List<Integer> pending = new ArrayList<>();
85
        List<Integer> rejected = new ArrayList<>();
86
        Set<Tenure> collected = new LinkedHashSet<>();
87
 
88
        Matcher m = TOKEN.matcher(upper);
89
        while (m.find()) {
90
            String token = m.group();
91
            if (Character.isDigit(token.charAt(0))) {
92
                pending.add(Integer.parseInt(token));
93
                continue;
94
            }
95
            String scheme = scheme(token);
96
            if (scheme != null) {
97
                flush(pending, scheme, collected, rejected);
98
            } else if ("M".equals(token)) {
99
                flush(pending, "NONE", collected, rejected);
100
            }
101
        }
102
        // trailing months with no marker: CIB if the cell mentions it, else unscheme
103
        if (!pending.isEmpty()) {
104
            flush(pending, upper.contains("CIB") ? "CIB" : "NONE", collected, rejected);
105
        }
106
 
107
        result.tenures.addAll(collected);
108
        result.tenures.sort((a, b) -> a.months != b.months
109
                ? Integer.compare(a.months, b.months)
110
                : a.scheme.compareTo(b.scheme));
111
        if (!rejected.isEmpty()) {
112
            result.warning = "bad-tenure:" + rejected;
113
        }
114
        return result;
115
    }
116
 
117
    private static void flush(List<Integer> pending, String scheme,
118
                              Set<Tenure> collected, List<Integer> rejected) {
119
        for (Integer months : pending) {
120
            if (VALID_MONTHS.contains(months)) {
121
                collected.add(new Tenure(months, scheme));
122
            } else {
123
                rejected.add(months);
124
            }
125
        }
126
        pending.clear();
127
    }
128
}