package com.celements.docform; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xwiki.model.reference.DocumentReference; import com.celements.web.service.IWebUtilsService; import com.xpn.xwiki.web.Utils; public class DocFormRequestKeyParser { private static Logger LOGGER = LoggerFactory.getLogger(DocFormRequestKeyParser.class); public static final String KEY_DELIM = "_"; public static final String REGEX_FULLNAME = "[a-zA-Z0-9]+\\.[a-zA-Z0-9]+"; public static final String REGEX_OBJNB = "[-^]?(\\d)*"; public static final String REGEX_WHITELIST = "content|title"; /** * Parses a given key string to {@link DocFormRequestKey} object * * @param key * syntax: "[{fullName}_]{className}_[-|^]{objNb}_{fieldName}" whereas fullName * is optional and objNb can be preceded by "-" to create or "^" to delete the * object * @param defaultDocRef * used if no fullName is provided in the key * @return key or null */ public DocFormRequestKey parse(String key, DocumentReference defaultDocRef) { List<String> keyParts = new ArrayList<String>(Arrays.asList(key.split(KEY_DELIM))); DocFormRequestKey ret; DocumentReference docRef = parseDocRef(keyParts, defaultDocRef); if (isWhiteListKey(keyParts)) { String fieldName = parseFieldName(keyParts); ret = new DocFormRequestKey(key, docRef, null, false, null, fieldName); } else if (isPropertyKey(keyParts)) { DocumentReference classRef = parseClassRef(keyParts); boolean remove = parseRemove(keyParts); Integer objNb = parseObjNb(keyParts); String fieldName = parseFieldName(keyParts); ret = new DocFormRequestKey(key, docRef, classRef, remove, objNb, fieldName); } else { LOGGER.info("parse: skipped key '{}'", key); ret = null; } if (validate(ret)) { return ret; } else { throw new IllegalArgumentException("Illegal request key '" + ret + "'"); } } private DocumentReference parseDocRef(List<String> keyParts, DocumentReference defaultDocRef) { DocumentReference ret = null; if ((keyParts.size() > 1) && (keyParts.get(1).matches(REGEX_FULLNAME) || keyParts.get( 1).matches(REGEX_WHITELIST))) { String fullName = keyParts.remove(0); if (fullName.matches(REGEX_FULLNAME)) { ret = getWebUtils().resolveDocumentReference(fullName); } } else { ret = defaultDocRef; } return ret; } private boolean isWhiteListKey(List<String> keyParts) { return (keyParts.size() == 1) && keyParts.get(0).matches(REGEX_WHITELIST); } private boolean isPropertyKey(List<String> keyParts) { return (keyParts.size() > 1) && keyParts.get(0).matches(REGEX_FULLNAME) && !keyParts.get( 1).matches("nb"); // to skip "{classeName}_nb" keys } private DocumentReference parseClassRef(List<String> keyParts) { DocumentReference ret = null; if (getFirst(keyParts).matches(REGEX_FULLNAME)) { ret = getWebUtils().resolveDocumentReference(keyParts.remove(0)); } return ret; } private boolean parseRemove(List<String> keyParts) { boolean ret = false; String str = getFirst(keyParts); if (str.matches(REGEX_OBJNB) && str.startsWith("^")) { keyParts.set(0, str.substring(1, str.length())); ret = true; } return ret; } private Integer parseObjNb(List<String> keyParts) { Integer ret = null; if (getFirst(keyParts).matches(REGEX_OBJNB)) { String intStr = keyParts.remove(0); try { ret = Integer.parseInt(intStr); } catch (NumberFormatException exc) { LOGGER.trace("Unable to parse: " + intStr, exc); } } return ret; } private String parseFieldName(List<String> keyParts) { return StringUtils.join(keyParts.iterator(), KEY_DELIM); } private boolean validate(DocFormRequestKey key) { boolean valid; if (key != null) { valid = (key.getDocRef() != null); if (!key.isWhiteListed()) { valid &= (key.getClassRef() != null); valid &= (key.getObjNb() != null); if (!key.isRemove()) { valid &= StringUtils.isNotBlank(key.getFieldName()); } } } else { valid = true; } return valid; } /** * Parses given key strings to {@link DocFormRequestKey} objects. See * {@link #parse(String, DocumentReference)} * * @param keys * @param defaultDocRef * @return list of keys with no null elements */ public Collection<DocFormRequestKey> parse(Collection<String> keys, DocumentReference defaultDocRef) { Collection<DocFormRequestKey> ret = new ArrayList<DocFormRequestKey>(); for (String keyString : keys) { if (StringUtils.isNotBlank(keyString)) { DocFormRequestKey key = parse(keyString, defaultDocRef); if ((key != null) && filterRequestKeySetForRemove(ret, key)) { ret.add(key); } } } return ret; } private boolean filterRequestKeySetForRemove(Collection<DocFormRequestKey> keys, DocFormRequestKey key) { boolean ret = true; Iterator<DocFormRequestKey> iter = keys.iterator(); while (iter.hasNext()) { DocFormRequestKey currKey = iter.next(); if (key.sameObject(currKey)) { if (key.isRemove()) { iter.remove(); } else if (currKey.isRemove()) { ret = false; } } } return ret; } private String getFirst(List<String> list) { if (list.size() > 0) { return list.get(0); } return ""; } private IWebUtilsService getWebUtils() { return Utils.getComponent(IWebUtilsService.class); } }