package io.ebeaninternal.server.querydefn; import io.ebean.FetchConfig; import io.ebean.util.StringHelper; import java.util.LinkedHashSet; /** * Parses the path properties string. */ class OrmQueryPropertiesParser { private static Response EMPTY = new Response(); /** * Immutable response of the parsed properties and options. */ static class Response { final boolean readOnly; final boolean cache; final FetchConfig fetchConfig; final String properties; final LinkedHashSet<String> included; Response(boolean readOnly, boolean cache, int queryFetchBatch, int lazyFetchBatch, String properties, LinkedHashSet<String> included) { this.readOnly = readOnly; this.cache = cache; this.properties = properties; this.included = included; if (lazyFetchBatch > -1 || queryFetchBatch > -1) { this.fetchConfig = new FetchConfig().lazy(lazyFetchBatch).query(queryFetchBatch); } else { this.fetchConfig = OrmQueryProperties.DEFAULT_FETCH; } } Response() { this.readOnly = false; this.cache = false; this.fetchConfig = OrmQueryProperties.DEFAULT_FETCH; this.properties = ""; this.included = null; } } /** * Parses the path properties string returning the parsed properties and options. * In general it is comma delimited with some special strings like +lazy(20). */ public static Response parse(String rawProperties) { return new OrmQueryPropertiesParser(rawProperties).parse(); } private String inputProperties; private String outputProperties = ""; private boolean allProperties; private boolean readOnly; private boolean cache; private int queryFetchBatch = -1; private int lazyFetchBatch = -1; private OrmQueryPropertiesParser(String inputProperties) { this.inputProperties = inputProperties; } /** * Parse the raw string properties input. */ private Response parse() { if (inputProperties == null || inputProperties.isEmpty()) { return EMPTY; } int pos = inputProperties.indexOf("+readonly"); if (pos > -1) { inputProperties = StringHelper.replaceString(inputProperties, "+readonly", ""); readOnly = true; } pos = inputProperties.indexOf("+cache"); if (pos > -1) { inputProperties = StringHelper.replaceString(inputProperties, "+cache", ""); cache = true; } pos = inputProperties.indexOf("+query"); if (pos > -1) { queryFetchBatch = parseBatchHint(pos, "+query"); } pos = inputProperties.indexOf("+lazy"); if (pos > -1) { lazyFetchBatch = parseBatchHint(pos, "+lazy"); } LinkedHashSet<String> included = parseIncluded(); String properties = (allProperties) ? "*" : outputProperties; return new Response(readOnly, cache, queryFetchBatch, lazyFetchBatch, properties, included); } /** * Parse the include separating by comma or semicolon. */ private LinkedHashSet<String> parseIncluded() { inputProperties = inputProperties.trim(); if (inputProperties.isEmpty()) { // default properties return null; } if (inputProperties.equals("*")) { // explicit all properties allProperties = true; return null; } String[] res = inputProperties.split(","); StringBuilder sb = new StringBuilder(70); LinkedHashSet<String> propertySet = new LinkedHashSet<>(res.length * 2); int count = 0; String temp; for (String re : res) { temp = re.trim(); if (!temp.isEmpty()) { if (count > 0) { sb.append(","); } sb.append(temp); propertySet.add(temp); count++; } } if (propertySet.isEmpty()) { // default properties return null; } if (propertySet.contains("*")) { // explicit all properties allProperties = true; return null; } // partial properties outputProperties = sb.toString(); return propertySet; } private int parseBatchHint(int pos, String option) { int startPos = pos + option.length(); int endPos = findEndPos(startPos, inputProperties); if (endPos == -1) { inputProperties = StringHelper.replaceString(inputProperties, option, ""); return 0; } else { String batchParam = inputProperties.substring(startPos + 1, endPos); if (endPos + 1 >= inputProperties.length()) { inputProperties = inputProperties.substring(0, pos); } else { inputProperties = inputProperties.substring(0, pos) + inputProperties.substring(endPos + 1); } return Integer.parseInt(batchParam); } } private int findEndPos(int pos, String props) { if (pos < props.length()) { if (props.charAt(pos) == '(') { int endPara = props.indexOf(')', pos + 1); if (endPara == -1) { throw new RuntimeException("Error could not find ')' in " + props + " after position " + pos); } return endPara; } } return -1; } }