Java tutorial
/* * Copyright 2017 Atypon Systems, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.atypon.wayf.database; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.commons.beanutils.BeanUtilsBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; class QueryMapper { private static final Logger LOG = LoggerFactory.getLogger(QueryMapper.class); private static final Set<String> FIELD_BLACKLIST = Sets.newHashSet(DbExecutor.LIMIT, DbExecutor.OFFSET); private static final String DELIMITER = "."; private static final String REGEX_DELIMITER = "\\."; private static final String FIELD_REGEX = ":([a-zA-Z0-9\\.]+)?"; private static final Pattern PATTERN = Pattern.compile(FIELD_REGEX, Pattern.DOTALL | Pattern.MULTILINE); private static final Map<String, List<String>> parsedQueryCache = new HashMap<>(); // Don't let this grow too big private static BeanUtilsBean beanUtilsBean = new BeanUtilsBean(); public static Map<String, Object> buildQueryArguments(String query, Object bean) { List<String> parsedQuery = parseQuery(query); Map<String, Object> values = new HashMap<>(); for (String queryField : parsedQuery) { values.put(queryField, getValue(queryField, bean)); } return values; } private static List<String> parseQuery(String query) { List<String> parsedQuery = parsedQueryCache.get(query); if (parsedQuery == null) { Matcher result = PATTERN.matcher(query); parsedQuery = new LinkedList<>(); while (result.find()) { for (int i = 1; i <= result.groupCount(); i++) { String field = result.group(i); if (!FIELD_BLACKLIST.contains(field)) { parsedQuery.add(field); } } } parsedQueryCache.put(query, parsedQuery); } return parsedQuery; } private static Object getValue(String field, Object bean) { LOG.debug("Getting value for field [{}] of bean [{}]", field, bean); try { Object fieldValue = field.contains(DELIMITER) ? handleNestedValue(bean, field.split(REGEX_DELIMITER), 0) : beanUtilsBean.getPropertyUtils().getProperty(bean, field); LOG.debug("Found value [{}] for field [{}] of bean [{}]", fieldValue, field, bean); return getDbValue(fieldValue); } catch (Exception e) { LOG.error("Could not parse argument [{}] from bean [{}]", field, bean); throw new RuntimeException(e); } } private static Object handleNestedValue(Object bean, String[] path, int index) throws Exception { LOG.debug("Handling nested value for bean[{}] path[{}] index[{}]", bean, path, index); String fieldName = path[index]; if (index == path.length - 1) { LOG.debug("Returning nested field [{}] of bean [{}]", fieldName, bean); return beanUtilsBean.getPropertyUtils().getProperty(bean, fieldName); } else { Object childBean = beanUtilsBean.getPropertyUtils().getProperty(bean, fieldName); if (childBean == null) { return null; } LOG.debug("Recursing for childBean bean[{}] field[{}]", childBean, fieldName); return handleNestedValue(childBean, path, ++index); } } private static Object getDbValue(Object value) { if (value == null) { return null; } if (value.getClass().isArray()) { List<Object> values = new LinkedList<>(); for (Object arrayElem : ((Object[]) value)) { values.add(getDbValue(arrayElem)); } return values; } if (value.getClass().isEnum()) { return value.toString(); } if (value.getClass().equals(UUID.class)) { return value.toString(); } return value; } }