migrate sdnr features to phosphorus
[ccsdk/features.git] / sdnr / wt / data-provider / dblib / src / main / java / org / onap / ccsdk / features / sdnr / wt / dataprovider / database / sqldb / query / SqlQuery.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  */
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query;
23
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Calendar;
27 import java.util.Date;
28 import java.util.List;
29 import java.util.TimeZone;
30 import java.util.stream.Collectors;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.onap.ccsdk.features.sdnr.wt.common.database.data.DbFilter;
33 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query.filters.DBFilterKeyValuePair;
34 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query.filters.RangeSqlDBFilter;
35 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query.filters.RegexSqlDBFilter;
36 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.NetconfTimeStamp;
37 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.types.NetconfTimeStampImpl;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.entity.input.Filter;
39
40 public interface SqlQuery {
41
42     String toSql();
43
44     static final List<String> TIMESTAMPPROPERTYNAMES = Arrays.asList("timestamp", "time-stamp", "start", "end");
45     static final String MARIADB_TIMESTAMP_REPLACER = "0000-00-00 00:00:00.000";
46     static final String NETCONF_TIMESTAMP_REPLACER = "0000-00-00T00:00:00.000Z";
47     static final String MARIADB_TIMESTAMP_REPLACER_MIN = "0000-00-00 00:00:00";
48     static final int MARIADB_TIMESTAMP_REPLACER_MIN_LENGTH = MARIADB_TIMESTAMP_REPLACER_MIN.length();
49     static final int MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH = MARIADB_TIMESTAMP_REPLACER.length();
50     static final boolean DEFAULT_IGNORE_CONTROLLERID = false;
51     static final boolean DEFAULT_IGNORE_ID_FIELD = false;
52
53     public static String getWhereExpression(List<Filter> filters) {
54         if (filters == null) {
55             return "";
56         }
57         StringBuilder sb = new StringBuilder();
58         filters = filters.stream().filter(e -> !"*".equals(e.getFiltervalue())).collect(Collectors.toList());
59         if (!filters.isEmpty()) {
60
61             sb.append(" WHERE (" + getFilterExpression(filters.get(0)) + ")");
62             for (int i = 1; i < filters.size(); i++) {
63                 sb.append(" AND (" + getFilterExpression(filters.get(i)) + ")");
64             }
65         }
66         return sb.toString();
67     }
68
69     public static String getFilterExpression(Filter filter) {
70         String property = filter.getProperty();
71         List<String> values = collectValues(filter.getFiltervalue(), filter.getFiltervalues()).stream()
72                 .filter(e -> !"*".equals(e)).collect(Collectors.toList());
73         if (values.size() == 1) {
74             return getFilterExpression(property, values.get(0));
75         } else if (values.size() > 1) {
76             StringBuilder sb = new StringBuilder();
77             sb.append(getFilterExpression(property, values.get(0)));
78             for (int i = 1; i < values.size(); i++) {
79                 sb.append(" OR ");
80                 sb.append(getFilterExpression(property, values.get(i)));
81             }
82             return sb.toString();
83         }
84         return null;
85     }
86
87     public static String getFilterExpression(String property, String value) {
88         String filter = null;
89         if (DbFilter.hasSearchParams(value)) {
90             if (TIMESTAMPPROPERTYNAMES.contains(property.toLowerCase())) {
91                 if (DbFilter.isComparisonValid(value)) {
92                     filter = getComparisonFilter(property, value, true);
93                 } else {
94                     filter = fromTimestampSearchFilter(property, value);
95                 }
96                 if (filter != null) {
97                     return filter;
98                 }
99             }
100             return new RegexSqlDBFilter(property, value).getFilterExpression();
101         } else if (DbFilter.isComparisonValid(value)) {
102             filter = getComparisonFilter(property, value, TIMESTAMPPROPERTYNAMES.contains(property.toLowerCase()));
103             if (filter != null) {
104                 return filter;
105             }
106         }
107         return new DBFilterKeyValuePair(property, value).getFilterExpression();
108     }
109
110     static List<String> collectValues(String filtervalue, List<String> filtervalues) {
111         if (filtervalues == null) {
112             return Arrays.asList(filtervalue);
113         }
114         List<String> values = new ArrayList<>();
115         if (filtervalue != null) {
116             values.add(filtervalue);
117         }
118         values.addAll(filtervalues);
119         return values;
120     }
121
122     private static String getComparisonFilter(String property, String filtervalue, boolean asTimeStamp) {
123         filtervalue = filtervalue.trim();
124         String comparator = null;
125         Object value;
126         if (filtervalue.startsWith(">=")) {
127             comparator = ">=";
128             filtervalue = filtervalue.substring(2).trim();
129             if (asTimeStamp) {
130                 filtervalue = netconfToMariaDBTimestamp(fillTimeStamp(
131                         filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"), MARIADB_TIMESTAMP_REPLACER));
132             }
133         } else if (filtervalue.startsWith(">")) {
134             comparator = ">";
135             filtervalue = filtervalue.substring(1).trim();
136             if (asTimeStamp) {
137                 if (isFullTimestamp(filtervalue)) {
138                     filtervalue = netconfToMariaDBTimestamp(filtervalue);
139                 } else {
140                     comparator = ">=";
141                     filtervalue = netconfToMariaDBTimestamp(
142                             fillTimeStamp(filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"),
143                                     NETCONF_TIMESTAMP_REPLACER, true));
144                 }
145             }
146         } else if (filtervalue.startsWith("<=")) {
147             comparator = "<=";
148             filtervalue = filtervalue.substring(2).trim();
149             if (asTimeStamp) {
150                 if (isFullTimestamp(filtervalue)) {
151                     filtervalue = netconfToMariaDBTimestamp(filtervalue);
152                 } else {
153                     comparator = "<";
154                     filtervalue = netconfToMariaDBTimestamp(
155                             fillTimeStamp(filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"),
156                                     NETCONF_TIMESTAMP_REPLACER, true));
157                 }
158             }
159         } else if (filtervalue.startsWith("<")) {
160             comparator = "<";
161             filtervalue = filtervalue.substring(1).trim();
162             if (asTimeStamp) {
163                 filtervalue = netconfToMariaDBTimestamp(fillTimeStamp(
164                         filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"), MARIADB_TIMESTAMP_REPLACER));
165             }
166         } else {
167             return null;
168         }
169         value = filtervalue;
170         return new RangeSqlDBFilter(property, value, comparator).getFilterExpression();
171     }
172
173     static boolean isFullTimestamp(String v) {
174         return v.length() >= MARIADB_TIMESTAMP_REPLACER_MIN_LENGTH;
175     }
176
177     /**
178      * Convert timestamp beginning filter expression like 2017* to a full qualified timestamp like '2017-01-01
179      * 00:00:00'.
180      *
181      * @param value filter input value
182      * @return fully qualified timestamp
183      */
184     private static String fillTimeStamp(String value) {
185         return fillTimeStamp(value, NETCONF_TIMESTAMP_REPLACER);
186     }
187
188     private static String fillTimeStamp(String value, String replacer) {
189         return fillTimeStamp(value, replacer, false);
190     }
191
192     private static String fillTimeStamp(String value, String replacer, boolean useUpperEnd) {
193         int idx = value.lastIndexOf("*");
194         String s = null;
195         if (idx > replacer.length()) {
196             s = value.substring(0, replacer.length());
197         } else {
198             s = value.substring(0, idx) + replacer.substring(idx);
199         }
200         //if month is zero => set to 1
201         if (Integer.parseInt(s.substring(5, 7)) == 0) {
202             s = s.substring(0, 5) + "01-" + s.substring(8);
203         }
204         //if day is zero => set to 1
205         if (Integer.parseInt(s.substring(8, 10)) == 0) {
206             s = s.substring(0, 8) + "01" + s.substring(10);
207         }
208         if (useUpperEnd) {
209             s = getTimestampUpperLimit(s, idx);
210         }
211         return s;
212     }
213
214     /**
215      * convert timestamp with ending placeholder in filter to elasticsearch filter e.g. 2017* => gte: 2017-01-01
216      * 00:00:00, lt:2018-01-01 00:00:00Z
217      *
218      * 201* => 2010-01... 2020 .. 2018-* => 2018-01... <=> 2019-01
219      *
220      */
221     private static @Nullable String fromTimestampSearchFilter(String property, String value) {
222         if (!value.endsWith("*")) {
223             return null;
224         }
225         int idx = value.lastIndexOf("*");
226         String lowerEnd = fillTimeStamp(value);
227         String upperEnd = getTimestampUpperLimit(fillTimeStamp(value, "0000-00-00T00:00:00.0Z"), idx);
228         return RangeSqlDBFilter.between(property, netconfToMariaDBTimestamp(lowerEnd), true,
229                 netconfToMariaDBTimestamp(upperEnd), false);
230     }
231
232     private static String netconfToMariaDBTimestamp(String ts) {
233         if (ts == null) {
234             return null;
235         }
236         String v = ts.replace("T", " ").replace("Z", "");
237         return v.length() > MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH
238                 ? v.substring(0, MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH)
239                 : v;
240     }
241
242     private static String getTimestampUpperLimit(String lowerEnd, int idx) {
243
244         String upperEnd = null;
245         NetconfTimeStamp converter = NetconfTimeStampImpl.getConverter();
246         Date dt = null;
247         try {
248             dt = converter.getDateFromNetconf(lowerEnd);
249         } catch (Exception e) {
250
251         }
252         if (dt == null) {
253             return null;
254         }
255
256         Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
257         c.setTime(dt);
258         int tmpvalue;
259         switch (idx) {
260             case 1: // (2*)
261                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1000);
262                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
263                 break;
264             case 2: // (20*)
265                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 100);
266                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
267                 break;
268             case 3: // (200*)
269                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 10);
270                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
271                 break;
272             case 4: // (2000*)
273             case 5: // (2000-*)
274                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
275                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
276                 break;
277             case 6: // switch 10 months (2000-0* or 2000-1*)
278                 tmpvalue = c.get(Calendar.MONTH);
279                 if (tmpvalue < 9) {
280                     c.set(Calendar.MONTH, 9);
281                 } else {
282                     c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
283                     c.set(Calendar.MONTH, 0);
284                 }
285                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
286
287                 break;
288             case 7: // switch one month (2018-01* or 2018-01-*)
289             case 8:
290                 c.add(Calendar.MONTH, 1);
291                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
292                 break;
293             case 9: // (2018-01-0*)
294                 tmpvalue = c.get(Calendar.DAY_OF_MONTH);
295                 if (tmpvalue == 1) {
296                     c.set(Calendar.DAY_OF_MONTH, 10);
297                 } else if (tmpvalue == 10) {
298                     c.set(Calendar.DAY_OF_MONTH, 20);
299                 } else if (tmpvalue == 20) {
300                     if (c.getActualMaximum(Calendar.DAY_OF_MONTH) < 30) {
301                         c.set(Calendar.DAY_OF_MONTH, 1);
302                         c.add(Calendar.MONTH, 1);
303                     } else {
304                         c.set(Calendar.DAY_OF_MONTH, 30);
305                     }
306                 } else if (tmpvalue == 30) {
307                     c.set(Calendar.DAY_OF_MONTH, 1);
308                     c.add(Calendar.MONTH, 1);
309                 } else {
310                     break;
311                 }
312                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
313                 break;
314             case 10: // (2018-01-01*)
315             case 11: // (2018-01-01T*)
316                 c.add(Calendar.DAY_OF_MONTH, 1);
317                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
318                 break;
319             case 12: // (2018-01-01T1*)
320                 tmpvalue = c.get(Calendar.HOUR_OF_DAY);
321                 if (tmpvalue == 20) {
322                     c.set(Calendar.HOUR_OF_DAY, 0);
323                     c.add(Calendar.DAY_OF_MONTH, 1);
324                 } else {
325                     c.add(Calendar.HOUR_OF_DAY, 10);
326                 }
327                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
328                 break;
329             case 13: // (2018-01-01T11*)
330             case 14: // (2018-01-01T11-*)
331                 c.add(Calendar.HOUR_OF_DAY, 1);
332                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
333                 break;
334             case 15: // (2018-01-01T11-3*)
335                 c.add(Calendar.MINUTE, 10);
336                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
337                 break;
338             case 16: // (2018-01-01T11-32*)
339             case 17: // (2018-01-01T11-32-*)
340                 c.add(Calendar.MINUTE, 1);
341                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
342                 break;
343             case 18: // (2018-01-01T11-32-1*)
344                 c.add(Calendar.SECOND, 10);
345                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
346                 break;
347             case 19: // (2018-01-01T11-32-11*)
348             case 20: // (2018-01-01T11-32-11.*)
349                 c.add(Calendar.SECOND, 1);
350                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
351                 break;
352
353             default:
354                 break;
355         }
356         return upperEnd;
357     }
358
359 }