package com.bagri.core.server.api.impl; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.xquery.XQItemType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bagri.core.api.BagriException; import com.bagri.core.model.NodeKind; import com.bagri.core.model.Occurrence; import com.bagri.core.model.Path; import com.bagri.core.server.api.ModelManagement; import com.bagri.support.idgen.IdGenerator; /** * Base implementation for XDM Model Management interface. Very close to its client ancestor class. * * @author Denis Sukhoroslov * */ public abstract class ModelManagementBase implements ModelManagement { protected final transient Logger logger = LoggerFactory.getLogger(getClass()); protected static final long timeout = 100; // 100ms to wait for lock.. protected abstract Map<String, Path> getPathCache(); protected abstract IdGenerator<Long> getPathGen(); //protected abstract <K> boolean lock(Map<K, ?> cache, K key); //protected abstract <K> void unlock(Map<K, ?> cache, K key); protected abstract <K, V> V putIfAbsent(Map<K, V> cache, K key, V value); //protected abstract <K, V> V putPathIfAbsent(Map<K, V> cache, K key, V value); protected abstract Set<Map.Entry<String, Path>> getTypedPathEntries(String root); protected abstract Set<Map.Entry<String, Path>> getTypedPathWithRegex(String regex, String root); /** * WRONG_PATH identifies path not existing in XDMPath dictionary */ public static final int WRONG_PATH = -1; protected String getPathKey(String root, String path) { return root + ":" + path; } /** * search for registered full node path like "/{http://tpox-benchmark.com/security}Security/{http://tpox-benchmark.com/security}Name/text()" * * @param path String; node path in Clark form * @return registered {@link Path} structure if any */ public Path getPath(String root, String path) { String pathKey = getPathKey(root, path); return getPathCache().get(pathKey); } /** * translates full node path like "/{http://tpox-benchmark.com/security}Security/{http://tpox-benchmark.com/security}Name/text()" * to XDMPath; * * creates new XDMPath if it is not registered yet; * * @param root String; the corresponding document's root * @param path String; the full node path in Clark form * @param kind XDMNodeKind; the type of the node, one of {@link NodeKind} enum literals * @param dataType int; type of the node value * @param occurrence {@link Occurrence}; multiplicity of the node * @return new or existing {@link Path} structure * @throws BagriException in case of any error */ public Path translatePath(String root, String path, NodeKind kind, int parentId, int dataType, Occurrence occurrence) throws BagriException { // "/{http://tpox-benchmark.com/security}Security/{http://tpox-benchmark.com/security}Name/text()" //if (kind != NodeKind.document) { if (path == null || path.length() == 0) { return null; //WRONG_PATH; } //path = normalizePath(path); //} Path result = addDictionaryPath(root, path, kind, parentId, dataType, occurrence); return result; } /** * * @param path the long element path * @return the path root */ public String getPathRoot(String path) { if (path.startsWith("/{")) { int pos = path.indexOf("/", path.indexOf("}")); if (pos > 0) { return path.substring(0, path.indexOf("/", path.indexOf("}"))); } return path; } String[] segments = path.split("/"); if (segments.length > 1) { return "/" + segments[1]; } else if (segments.length > 0) { return "/" + segments[0]; } return null; } /** * return array of pathIds which are children of the root specified; * * @param root String; root node path * @return Set<Integer>- set of registered pathIds who are direct or indirect children of the parent path provided */ public Set<Integer> getPathElements(String root) { logger.trace("getPathElements.enter; got root: {}", root); Set<Integer> result = new HashSet<Integer>(); String pathKey = getPathKey(getPathRoot(root), root); Path xPath = getPathCache().get(pathKey); if (xPath != null) { int pId = xPath.getPathId(); while (pId <= xPath.getPostId()) { result.add(pId); pId++; } } logger.trace("getPathElements.exit; returning: {}", result); return result; } protected Path addDictionaryPath(String root, String path, NodeKind kind, int parentId, int dataType, Occurrence occurrence) throws BagriException { String pathKey = getPathKey(root, path); Path xpath = getPathCache().get(pathKey); if (xpath == null) { int pathId = getPathGen().next().intValue(); int postId = 0; if (kind == NodeKind.attribute || kind == NodeKind.comment || kind == NodeKind.namespace || kind == NodeKind.pi || kind == NodeKind.text) { postId = pathId; } xpath = new Path(path, root, kind, pathId, parentId, postId, dataType, occurrence); xpath = putIfAbsent(getPathCache(), pathKey, xpath); } if (parentId > 0 && xpath.getParentId() != parentId) { xpath.setParentId(parentId); updatePath(xpath); } return xpath; } public void updatePath(Path path) { String pathKey = getPathKey(path.getRoot(), path.getPath()); getPathCache().put(pathKey, path); } /** * translates regex expression like "^/ns0:Security/ns0:SecurityInformation/.(*)/ns0:Sector/text\\(\\)$"; * to an array of registered pathIds which conforms to the regex specified * * @param root String; the corresponding document's root * @param regex String; regex pattern * @return Set<Integer>- set of registered pathIds conforming to the pattern provided */ public Set<Integer> translatePathFromRegex(String root, String regex) { logger.trace("translatePathFromRegex.enter; got regex: {}, root: {}", regex, root); Set<Map.Entry<String, Path>> entries = getTypedPathWithRegex(regex, root); Set<Integer> result = new HashSet<Integer>(entries.size()); for (Map.Entry<String, Path> e: entries) { logger.trace("translatePathFromRegex; path found: {}", e.getValue()); result.add(e.getValue().getPathId()); } logger.trace("translatePathFromRegex.exit; returning: {}", result); return result; } /** * translates regex expression like "^/ns0:Security/ns0:SecurityInformation/.(*)/ns0:Sector/text\\(\\)$"; * to Collection of registered path which conforms to the regex specified * * @param root String; the corresponding document's type * @param regex String; regex pattern * @return Set<String>- set of registered paths conforming to the pattern provided */ public Collection<String> getPathFromRegex(String root, String regex) { logger.trace("getPathFromRegex.enter; got regex: {}, root: {}", regex, root); Set<Map.Entry<String, Path>> entries = getTypedPathWithRegex(regex, root); List<String> result = new ArrayList<String>(entries.size()); for (Map.Entry<String, Path> e: entries) { logger.trace("getPathFromRegex; path found: {}", e.getValue()); result.add(e.getKey()); } logger.trace("getPathFromRegex.exit; returning: {}", result); return result; } //protected int[] fromCollection(Collection<Integer> from) { // int idx = 0; // int[] result = new int[from.size()]; // for (Integer i: from) { // result[idx++] = i; // } // return result; //} }