2  * ============LICENSE_START=======================================================
 
   3  * ONAP : ccsdk features
 
   4  * ================================================================================
 
   5  * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
 
   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
 
  12  *     http://www.apache.org/licenses/LICENSE-2.0
 
  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=========================================================
 
  22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query;
 
  24 import java.util.ArrayList;
 
  25 import java.util.Arrays;
 
  26 import java.util.Calendar;
 
  27 import java.util.Collection;
 
  28 import java.util.Date;
 
  29 import java.util.List;
 
  31 import java.util.TimeZone;
 
  32 import java.util.stream.Collectors;
 
  33 import org.eclipse.jdt.annotation.Nullable;
 
  34 import org.onap.ccsdk.features.sdnr.wt.common.database.data.DbFilter;
 
  35 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.database.SqlDBMapper;
 
  36 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query.filters.DBFilterKeyValuePair;
 
  37 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query.filters.RangeSqlDBFilter;
 
  38 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.sqldb.query.filters.RegexSqlDBFilter;
 
  39 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.NetconfTimeStamp;
 
  40 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.types.NetconfTimeStampImpl;
 
  41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.entity.input.Filter;
 
  42 import io.netty.util.internal.StringUtil;
 
  44 public interface SqlQuery {
 
  48     static final List<String> TIMESTAMPPROPERTYNAMES = Arrays.asList("timestamp", "time-stamp", "start", "end");
 
  49     static final String MARIADB_TIMESTAMP_REPLACER = "0000-00-00 00:00:00.000";
 
  50     static final String NETCONF_TIMESTAMP_REPLACER = "0000-00-00T00:00:00.000Z";
 
  51     static final String MARIADB_TIMESTAMP_REPLACER_MIN = "0000-00-00 00:00:00";
 
  52     static final int MARIADB_TIMESTAMP_REPLACER_MIN_LENGTH = MARIADB_TIMESTAMP_REPLACER_MIN.length();
 
  53     static final int MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH = MARIADB_TIMESTAMP_REPLACER.length();
 
  54     static final boolean DEFAULT_IGNORE_CONTROLLERID = false;
 
  55     static final boolean DEFAULT_IGNORE_ID_FIELD = false;
 
  57     public static String getWhereExpression(Collection<Filter> filters) {
 
  58         return getWhereExpression(filters, null);
 
  60     public static String getWhereExpression(Collection<Filter> filters, String controllerId) {
 
  61         if (filters == null && controllerId == null) {
 
  64         StringBuilder sb = new StringBuilder();
 
  65         List<String> filters2 =
 
  67                         ? filters.stream().filter(e -> !"*".equals(e.getFiltervalue())).map(e -> getFilterExpression(e))
 
  68                                 .collect(Collectors.toList())
 
  70         if(controllerId!=null) {
 
  71             filters2.add(getFilterExpression(SqlDBMapper.ODLID_DBCOL, controllerId));
 
  73         if (!filters2.isEmpty() ) {
 
  75             sb.append(StringUtil.join(" AND ", filters2));
 
  80     public static String getFilterExpression(Filter filter) {
 
  81         String property = filter.getProperty();
 
  82         List<String> values = collectValues(filter.getFiltervalue(), filter.getFiltervalues()).stream()
 
  83                 .filter(e -> !"*".equals(e)).collect(Collectors.toList());
 
  84         if (values.size() == 1) {
 
  85             return getFilterExpression(property, values.get(0));
 
  86         } else if (values.size() > 1) {
 
  87             StringBuilder sb = new StringBuilder();
 
  88             sb.append(getFilterExpression(property, values.get(0)));
 
  89             for (int i = 1; i < values.size(); i++) {
 
  91                 sb.append(getFilterExpression(property, values.get(i)));
 
  98     public static String getFilterExpression(String property, String value) {
 
 100         if (DbFilter.hasSearchParams(value)) {
 
 101             if (TIMESTAMPPROPERTYNAMES.contains(property.toLowerCase())) {
 
 102                 if (DbFilter.isComparisonValid(value)) {
 
 103                     filter = getComparisonFilter(property, value, true);
 
 105                     filter = fromTimestampSearchFilter(property, value);
 
 107                 if (filter != null) {
 
 111             return new RegexSqlDBFilter(property, value).getFilterExpression();
 
 112         } else if (DbFilter.isComparisonValid(value)) {
 
 113             filter = getComparisonFilter(property, value, TIMESTAMPPROPERTYNAMES.contains(property.toLowerCase()));
 
 114             if (filter != null) {
 
 118         return new DBFilterKeyValuePair(property, value).getFilterExpression();
 
 121     static List<String> collectValues(String filtervalue, Set<String> filtervalues) {
 
 122         if (filtervalues == null) {
 
 123             return Arrays.asList(filtervalue);
 
 125         List<String> values = new ArrayList<>();
 
 126         if (filtervalue != null) {
 
 127             values.add(filtervalue);
 
 129         values.addAll(filtervalues);
 
 133     private static String getComparisonFilter(String property, String filtervalue, boolean asTimeStamp) {
 
 134         filtervalue = filtervalue.trim();
 
 135         String comparator = null;
 
 137         if (filtervalue.startsWith(">=")) {
 
 139             filtervalue = filtervalue.substring(2).trim();
 
 141                 filtervalue = netconfToMariaDBTimestamp(fillTimeStamp(
 
 142                         filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"), MARIADB_TIMESTAMP_REPLACER));
 
 144         } else if (filtervalue.startsWith(">")) {
 
 146             filtervalue = filtervalue.substring(1).trim();
 
 148                 if (isFullTimestamp(filtervalue)) {
 
 149                     filtervalue = netconfToMariaDBTimestamp(filtervalue);
 
 152                     filtervalue = netconfToMariaDBTimestamp(
 
 153                             fillTimeStamp(filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"),
 
 154                                     NETCONF_TIMESTAMP_REPLACER, true));
 
 157         } else if (filtervalue.startsWith("<=")) {
 
 159             filtervalue = filtervalue.substring(2).trim();
 
 161                 if (isFullTimestamp(filtervalue)) {
 
 162                     filtervalue = netconfToMariaDBTimestamp(filtervalue);
 
 165                     filtervalue = netconfToMariaDBTimestamp(
 
 166                             fillTimeStamp(filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"),
 
 167                                     NETCONF_TIMESTAMP_REPLACER, true));
 
 170         } else if (filtervalue.startsWith("<")) {
 
 172             filtervalue = filtervalue.substring(1).trim();
 
 174                 filtervalue = netconfToMariaDBTimestamp(fillTimeStamp(
 
 175                         filtervalue.endsWith("*") ? filtervalue : (filtervalue + "*"), MARIADB_TIMESTAMP_REPLACER));
 
 181         return new RangeSqlDBFilter(property, value, comparator).getFilterExpression();
 
 184     static boolean isFullTimestamp(String v) {
 
 185         return v.length() >= MARIADB_TIMESTAMP_REPLACER_MIN_LENGTH;
 
 189      * Convert timestamp beginning filter expression like 2017* to a full qualified timestamp like '2017-01-01
 
 192      * @param value filter input value
 
 193      * @return fully qualified timestamp
 
 195     private static String fillTimeStamp(String value) {
 
 196         return fillTimeStamp(value, NETCONF_TIMESTAMP_REPLACER);
 
 199     private static String fillTimeStamp(String value, String replacer) {
 
 200         return fillTimeStamp(value, replacer, false);
 
 203     private static String fillTimeStamp(String value, String replacer, boolean useUpperEnd) {
 
 204         int idx = value.lastIndexOf("*");
 
 206         if (idx > replacer.length()) {
 
 207             s = value.substring(0, replacer.length());
 
 209             s = value.substring(0, idx) + replacer.substring(idx);
 
 211         //if month is zero => set to 1
 
 212         if (Integer.parseInt(s.substring(5, 7)) == 0) {
 
 213             s = s.substring(0, 5) + "01-" + s.substring(8);
 
 215         //if day is zero => set to 1
 
 216         if (Integer.parseInt(s.substring(8, 10)) == 0) {
 
 217             s = s.substring(0, 8) + "01" + s.substring(10);
 
 220             s = getTimestampUpperLimit(s, idx);
 
 226      * convert timestamp with ending placeholder in filter to elasticsearch filter e.g. 2017* => gte: 2017-01-01
 
 227      * 00:00:00, lt:2018-01-01 00:00:00Z
 
 229      * 201* => 2010-01... 2020 .. 2018-* => 2018-01... <=> 2019-01
 
 232     private static @Nullable String fromTimestampSearchFilter(String property, String value) {
 
 233         if (!value.endsWith("*")) {
 
 236         int idx = value.lastIndexOf("*");
 
 237         String lowerEnd = fillTimeStamp(value);
 
 238         String upperEnd = getTimestampUpperLimit(fillTimeStamp(value, "0000-00-00T00:00:00.0Z"), idx);
 
 239         return RangeSqlDBFilter.between(property, netconfToMariaDBTimestamp(lowerEnd), true,
 
 240                 netconfToMariaDBTimestamp(upperEnd), false);
 
 243     private static String netconfToMariaDBTimestamp(String ts) {
 
 247         String v = ts.replace("T", " ").replace("Z", "");
 
 248         return v.length() > MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH
 
 249                 ? v.substring(0, MARIADB_TIMESTAMP_REPLACER_MAX_LENGTH)
 
 253     private static String getTimestampUpperLimit(String lowerEnd, int idx) {
 
 255         String upperEnd = null;
 
 256         NetconfTimeStamp converter = NetconfTimeStampImpl.getConverter();
 
 259             dt = converter.getDateFromNetconf(lowerEnd);
 
 260         } catch (Exception e) {
 
 267         Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
 
 272                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1000);
 
 273                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 276                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 100);
 
 277                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 280                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 10);
 
 281                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 285                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
 
 286                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 288             case 6: // switch 10 months (2000-0* or 2000-1*)
 
 289                 tmpvalue = c.get(Calendar.MONTH);
 
 291                     c.set(Calendar.MONTH, 9);
 
 293                     c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
 
 294                     c.set(Calendar.MONTH, 0);
 
 296                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 299             case 7: // switch one month (2018-01* or 2018-01-*)
 
 301                 c.add(Calendar.MONTH, 1);
 
 302                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 304             case 9: // (2018-01-0*)
 
 305                 tmpvalue = c.get(Calendar.DAY_OF_MONTH);
 
 307                     c.set(Calendar.DAY_OF_MONTH, 10);
 
 308                 } else if (tmpvalue == 10) {
 
 309                     c.set(Calendar.DAY_OF_MONTH, 20);
 
 310                 } else if (tmpvalue == 20) {
 
 311                     if (c.getActualMaximum(Calendar.DAY_OF_MONTH) < 30) {
 
 312                         c.set(Calendar.DAY_OF_MONTH, 1);
 
 313                         c.add(Calendar.MONTH, 1);
 
 315                         c.set(Calendar.DAY_OF_MONTH, 30);
 
 317                 } else if (tmpvalue == 30) {
 
 318                     c.set(Calendar.DAY_OF_MONTH, 1);
 
 319                     c.add(Calendar.MONTH, 1);
 
 323                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 325             case 10: // (2018-01-01*)
 
 326             case 11: // (2018-01-01T*)
 
 327                 c.add(Calendar.DAY_OF_MONTH, 1);
 
 328                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 330             case 12: // (2018-01-01T1*)
 
 331                 tmpvalue = c.get(Calendar.HOUR_OF_DAY);
 
 332                 if (tmpvalue == 20) {
 
 333                     c.set(Calendar.HOUR_OF_DAY, 0);
 
 334                     c.add(Calendar.DAY_OF_MONTH, 1);
 
 336                     c.add(Calendar.HOUR_OF_DAY, 10);
 
 338                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 340             case 13: // (2018-01-01T11*)
 
 341             case 14: // (2018-01-01T11-*)
 
 342                 c.add(Calendar.HOUR_OF_DAY, 1);
 
 343                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 345             case 15: // (2018-01-01T11-3*)
 
 346                 c.add(Calendar.MINUTE, 10);
 
 347                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 349             case 16: // (2018-01-01T11-32*)
 
 350             case 17: // (2018-01-01T11-32-*)
 
 351                 c.add(Calendar.MINUTE, 1);
 
 352                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 354             case 18: // (2018-01-01T11-32-1*)
 
 355                 c.add(Calendar.SECOND, 10);
 
 356                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 358             case 19: // (2018-01-01T11-32-11*)
 
 359             case 20: // (2018-01-01T11-32-11.*)
 
 360                 c.add(Calendar.SECOND, 1);
 
 361                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());